1 INTRODUCTION: FRAMINGHAM DATA SUBSET

The Framingham Heart Study, initiated in 1948, was designed to identify the factors and characteristics that contribute to the development of cardiovascular disease. Based in Massachusetts, the study has followed multiple generations of participants using a longitudinal design that includes regular examinations and health assessments. Participants undergo comprehensive medical evaluations, laboratory testing, and lifestyle assessments. The study captures a wide range of risk factors, including both behavioral and biological components.

This analysis focuses on a subset of data from the Framingham Heart Study. Specifically, it investigates how selected biological and behavioral risk factors predict the likelihood of developing coronary heart disease (CHD) within a ten-year period.

1.1 Description of Data

The original Framingham Heart Study includes data from approximately 15,000 participants and over 1,000 variables. For this analysis, a subset of the data is used, containing 4,240 observations and 16 variables.

This analysis focuses specifically on biological features that directly contribute to health outcomes. Prior analysis (https://jmusa3.github.io/jmusa/project/Project_2.html) indicated that education was not a significant predictor of either Ten-Year Coronary Heart Disease (CHD) risk or total cholesterol levels, supporting its removal from the feature set. As such, the variable “Education” has been excluded from the dataset.

The original feature “male” is changed to “sex” for ease of comprehension. After excluding “Education,” the dataset for this analysis consists of 15 predictor variables:

•   7 factor variables (categorical)
•   8 numeric variables (continuous)

The names and definitions of these variables are provided below.

  1. sex (factor): the sex of the observations: female (0), male (1).
  2. age (numerical): Age at the time of medical examination in years.
  3. currentSmoker (factor): Current cigarette smoking at the time of examinations
  4. cigsPerDay (numerical): Number of cigarettes smoked each day
  5. BPmeds (factor): Use of Anti-hypertensive medication at exam
  6. prevalentStroke (factor): Prevalent Stroke (0 = free of disease)
  7. prevalentHyp (factor): Prevalent Hypertensive. Subject was defined as hypertensive if treated
  8. diabetes (factor): Diabetic according to criteria of first exam treated
  9. totChol (numerical): Total cholesterol (mg/dL)
  10. sysBP (numerical): Systolic Blood Pressure (mmHg)
  11. diaBP (numerical): Diastolic blood pressure (mmHg)
  12. BMI (numerical): Body Mass Index, weight (kg)/height (m)^2
  13. heartRate (numerical): Heart rate (beats/minute)
  14. glucose (numerical): Blood glucose level (mg/dL)
  15. TenYearCHD (factor, response variable): The 10 year risk of coronary heart disease(CHD).

The data set has missing values for six variables: cigPerDay, BPMeds, totChol, BMI, heartRate, and glucose.

fhs <- read.csv("https://raw.githubusercontent.com/jmusa3/jmusa/refs/heads/main/FraminghamHeartStudy.csv")

fhs <- fhs %>% dplyr::select(-education)

# change 'male' to 'sex' where 1 = male and 0 = female
fhs <- fhs %>%
  rename(sex = male)
fhs$sex <- factor(fhs$sex)
fhs$BPMeds <- factor(fhs$BPMeds)
fhs$currentSmoker <- factor(fhs$currentSmoker)
fhs$prevalentStroke <- factor(fhs$prevalentStroke)
fhs$prevalentHyp <- factor(fhs$prevalentHyp)
fhs$diabetes <- factor(fhs$diabetes)

summary(fhs)
 sex           age        currentSmoker   cigsPerDay      BPMeds    
 0:2420   Min.   :32.00   0:2145        Min.   : 0.000   0   :4063  
 1:1820   1st Qu.:42.00   1:2095        1st Qu.: 0.000   1   : 124  
          Median :49.00                 Median : 0.000   NA's:  53  
          Mean   :49.58                 Mean   : 9.006              
          3rd Qu.:56.00                 3rd Qu.:20.000              
          Max.   :70.00                 Max.   :70.000              
                                        NA's   :29                  
 prevalentStroke prevalentHyp diabetes    totChol          sysBP      
 0:4215          0:2923       0:4131   Min.   :107.0   Min.   : 83.5  
 1:  25          1:1317       1: 109   1st Qu.:206.0   1st Qu.:117.0  
                                       Median :234.0   Median :128.0  
                                       Mean   :236.7   Mean   :132.4  
                                       3rd Qu.:263.0   3rd Qu.:144.0  
                                       Max.   :696.0   Max.   :295.0  
                                       NA's   :50                     
     diaBP            BMI          heartRate         glucose      
 Min.   : 48.0   Min.   :15.54   Min.   : 44.00   Min.   : 40.00  
 1st Qu.: 75.0   1st Qu.:23.07   1st Qu.: 68.00   1st Qu.: 71.00  
 Median : 82.0   Median :25.40   Median : 75.00   Median : 78.00  
 Mean   : 82.9   Mean   :25.80   Mean   : 75.88   Mean   : 81.96  
 3rd Qu.: 90.0   3rd Qu.:28.04   3rd Qu.: 83.00   3rd Qu.: 87.00  
 Max.   :142.5   Max.   :56.80   Max.   :143.00   Max.   :394.00  
                 NA's   :19      NA's   :1        NA's   :388     
   TenYearCHD    
 Min.   :0.0000  
 1st Qu.:0.0000  
 Median :0.0000  
 Mean   :0.1519  
 3rd Qu.:0.0000  
 Max.   :1.0000  
                 
#head(fhs)

Table 1. Summary statistics for the 15 features in the Framingham Heart Study subset. Six of the variables have missing values.

2 EDA

2.1 Single Features

All of the features in this data set are typed as numerical. In turn, indicator variables are changed to factor. For visualization, “male” is changed to “sex”. The indicator values for the categorical variables were changed to descriptive words or phrases to enhance readability of the graphs.

# Set up plotting area
par(mfrow = c(2, 2))

# Histogram for age
hist(fhs$age,
     main = "Age",
     xlab = "Age",
     col = "steelblue",
     border = "white")

# Histogram for cigsPerDay
hist(fhs$cigsPerDay,
     main = "Cigarettes Per Day",
     xlab = "Cigarettes Per Day",
     col = "steelblue",
     border = "white")

# Histogram for totChol
hist(fhs$totChol,
     main = "Total Cholesterol",
     xlab = "Total Cholesterol",
     col = "steelblue",
     border = "white")

# Histogram for sysBP
hist(fhs$sysBP,
     main = "Systolic Blood Pressure",
     xlab = "Systolic BP",
     col = "steelblue",
     border = "white")

# Reset plotting layout to default
par(mfrow = c(1, 1))

Figure A. Histograms of age, CigsPerDay, totChol (response), and sysBP.

# Set up 2x2 plotting area
par(mfrow = c(2, 2))

# Histogram for diastolic blood pressure
hist(fhs$diaBP,
     main = "Diastolic Blood Pressure",
     xlab = "Diastolic BP",
     col = "steelblue",
     border = "white")

# Histogram for BMI
hist(fhs$BMI,
     main = "Body Mass Index (BMI)",
     xlab = "BMI",
     col = "steelblue",
     border = "white")

# Histogram for heart rate
hist(fhs$heartRate,
     main = "Heart Rate",
     xlab = "Beats Per Minute",
     col = "steelblue",
     border = "white")

# Histogram for glucose
hist(fhs$glucose,
     main = "Glucose Level",
     xlab = "Glucose",
     col = "steelblue",
     border = "white")

# Reset to default plotting layout
par(mfrow = c(1, 1))

Figure B. Histograms for diaBP, BMI, heartRate, and glucose.

# Set up 2x2 plotting layout
par(mfrow = c(2, 2))


# Bar plot for sex
barplot(table(fhs$sex),
        main = "Sex Distribution",
        col = "steelblue",
        names.arg = c("Female", "Male"),
        ylab = "Count")



# Bar plot for current smoker
barplot(table(fhs$currentSmoker),
        main = "Current Smoker Status",
        col = "steelblue",
        names.arg = c("No", "Yes"),
        ylab = "Count")

# Bar plot for BP meds
barplot(table(fhs$BPMeds),
        main = "Blood Pressure Medication",
        col = "steelblue",
        names.arg = c("No", "Yes"),
        ylab = "Count")

Figure C. Bar charts for sex, currentSmoker and BPMeds.

# Set 2x2 layout for bar charts
par(mfrow = c(2, 2))

# Bar chart for prevalentStroke
barplot(table(fhs$prevalentStroke),
        main = "Prevalent Stroke",
        xlab = "0 = No, 1 = Yes",
        col = "steelblue")

# Bar chart for prevalentHyp
barplot(table(fhs$prevalentHyp),
        main = "Prevalent Hypertension",
        xlab = "0 = No, 1 = Yes",
        col = "steelblue")

# Bar chart for diabetes
barplot(table(fhs$diabetes),
        main = "Diabetes",
        xlab = "0 = No, 1 = Yes",
        col = "steelblue")

# Bar chart for TenYearCHD
barplot(table(fhs$TenYearCHD),
        main = "Ten-Year CHD",
        xlab = "0 = No, 1 = Yes",
        col = "steelblue")

Figure D. Bar charts for prevalentStroke, prevalentHyp, diabetes, and TenYearCHD (response).

3 MISSING VALUES

Imputation is a critical step in data preprocessing, especially when working with datasets containing missing values. It involves estimating and replacing missing data with plausible values based on the available information.

For the missing cigsPerDay values, any instances that are not current smokers (currentSmoker = 0) and have missing cigsPerDay will be imputed with zero. All other instances with missing cigsPerDay will be imputed with the mean.

# Step 1: Save the original cigsPerDay before imputation
cigs_before <- fhs$cigsPerDay

# Step 2: Impute using currentSmoker
mean_cigs_smokers <- mean(fhs$cigsPerDay[fhs$currentSmoker == 1 & !is.na(fhs$cigsPerDay)], na.rm = TRUE)
fhs$cigsPerDay[is.na(fhs$cigsPerDay) & fhs$currentSmoker == 0] <- 0
fhs$cigsPerDay[is.na(fhs$cigsPerDay) & fhs$currentSmoker == 1] <- mean_cigs_smokers

# Step 3: Create a combined data frame for plotting
cigs_df <- rbind(
  data.frame(value = cigs_before, group = "Before"),
  data.frame(value = fhs$cigsPerDay, group = "After")
)

# Step 4: Plot the overlay density curve
ggplot(cigs_df, aes(x = value, fill = group, color = group)) +
  geom_density(alpha = 0.5) +
  labs(title = "Density Plot of cigsPerDay Before and After Imputation",
       x = "Cigarettes Per Day", y = "Density") +
  scale_fill_manual(values = c("Before" = "lightblue", "After" = "salmon")) +
  scale_color_manual(values = c("Before" = "lightblue", "After" = "salmon")) +
  theme_minimal()

Figure E. Post imputation, cigsPerDay has zero missing values. The before and after imputation distributions are very similar.

The distributions of CigsPerDay before and after imputation are very similar, indicating that the imputation process effectively preserved the original data characteristics.

Multiple imputation by chained equations (MICE) is used to impute missing values for cigPerDay, BPMeds, totChol, BMI, heartRate, and glucose. MICE is an iterative method that creates multiple imputed datasets, improving the reliability of the imputation process by incorporating uncertainty in the missing values. The imputed values are expected to provide a more complete dataset for subsequent analysis, such as modeling and prediction.

# Create a data frame for the features before imputation (original data)
fhs_missing_before <- fhs[, c("cigsPerDay", "totChol", "BMI", "heartRate", "glucose")]

# Perform MICE imputation while fully suppressing console output
invisible(capture.output({
  mice_imputation <- mice(fhs_missing_before, method = 'pmm', m = 5, maxit = 50, seed = 500)
}))

# Complete the imputed data
fhs_imputed <- complete(mice_imputation, action = 1)

# Prepare data for plotting (before and after)
fhs_before <- data.frame(value = as.vector(as.matrix(fhs_missing_before)),
                         variable = rep(colnames(fhs_missing_before), each = nrow(fhs_missing_before)),
                         dataset = rep("Before", nrow(fhs_missing_before) * ncol(fhs_missing_before)))

fhs_after <- data.frame(value = as.vector(as.matrix(fhs_imputed)),
                        variable = rep(colnames(fhs_imputed), each = nrow(fhs_imputed)),
                        dataset = rep("After", nrow(fhs_imputed) * ncol(fhs_imputed)))

# Combine the data for before and after into one data frame
fhs_combined <- rbind(fhs_before, fhs_after)

# Create the density plot with ggplot
ggplot(fhs_combined, aes(x = value, fill = dataset, color = dataset)) +
  geom_density(alpha = 0.5) +
  facet_wrap(~variable, scales = "free") +
  labs(title = "Density Plots: Before and After Imputation", 
       x = "Value", y = "Density") +
  theme_minimal() +
  scale_fill_manual(values = c("Before" = "lightblue", "After" = "pink")) +
  scale_color_manual(values = c("Before" = "lightblue", "After" = "pink"))

Figure F. Before and after imputation density curves.

The distributions of the variables before and after imputation are very similar, indicating that the imputation process effectively preserved the original data characteristics. The use of multiple imputation by chained equations (MICE) has ensured that the imputed values closely follow the patterns and spread of the observed data, maintaining the integrity of the variables for further analysis. In turn, the imputed values is added to the final working dataset for analysis.

# Overwrite original columns in fhs with imputed values
fhs$cigsPerDay <- fhs_imputed$cigsPerDay
fhs$totChol <- fhs_imputed$totChol
fhs$BMI <- fhs_imputed$BMI
fhs$heartRate <- fhs_imputed$heartRate
fhs$glucose <- fhs_imputed$glucose

Prior to modeling, the variable BPMeds (whether the participant was on blood pressure medication) contains missing values. To address this, a logistic regression model is fitted using relevant predictors (age, sysBP, diaBP, cigsPerDay, and prevalentHyp) based on cases with observed BPMeds values. Predicted probabilities from this model were then used to impute missing values, with a threshold of 0.5 to classify individuals as either taking or not taking blood pressure medication.

# Save original BPMeds with NAs before imputation
fhs$BPMeds_original <- fhs$BPMeds

# Keep rows where BPMeds is not missing
bpm_complete <- fhs[!is.na(fhs$BPMeds_original), ]

# Fit logistic regression model
bpm_model <- glm(BPMeds_original ~ age + sysBP + diaBP + cigsPerDay + prevalentHyp,
                 data = bpm_complete, family = binomial)

# Identify rows with missing BPMeds
bpm_missing <- fhs[is.na(fhs$BPMeds_original), ]

# Identify rows among bpm_missing that have complete predictors
predictable_rows <- complete.cases(bpm_missing[, c("age", "sysBP", "diaBP", "cigsPerDay", "prevalentHyp")])

# Predict only on rows we can predict
if (any(predictable_rows)) {
  predicted_probs <- predict(bpm_model, newdata = bpm_missing[predictable_rows, ], type = "response")
  
  # Get the original row numbers for the missing data
  missing_row_indices <- which(is.na(fhs$BPMeds_original))
  
  # Find indices that are predictable
  predictable_indices <- missing_row_indices[predictable_rows]
  
  # Impute predicted values into the correct rows
  fhs$BPMeds[predictable_indices] <- ifelse(predicted_probs >= 0.5, 1, 0)
}


# Make sure fhs$BPMeds and fhs$BPMeds_original are there

# Create a long-format data frame
bpm_plot_data <- data.frame(
  value = c(fhs$BPMeds_original, fhs$BPMeds),
  status = rep(c("Before Imputation", "After Imputation"), each = nrow(fhs))
)

# Clean bar plot
ggplot(bpm_plot_data, aes(x = factor(value), fill = status)) +
  geom_bar(position = "dodge") +
  labs(title = "BPMeds Before and After Imputation",
       x = "BPMeds (0 = No, 1 = Yes)",
       y = "Count",
       fill = "Status") +
  scale_x_discrete(labels = c("0" = "No", "1" = "Yes")) +
  theme_minimal(base_size = 14)

Figure G. Side by side bar chart of before and after logistic imputation.

The distribution of BPMeds before and after logistic imputation is very similar, indicating that the imputation process preserved the underlying structure of the data without introducing substantial bias.

# Remove BPMeds_original column after imputation and plotting
fhs$BPMeds_original <- NULL

4 FEATURE SELECTION

4.1 Feature Selection

To reduce the number of variables included in pairwise relationship plots, feature selection was performed using a Random Forest regression model. The goal is to identify the most important numerical predictors of TenYearCHD, so that only the most relevant variables are examined in scatterplots and other visualizations.

# Subset numeric predictors
numeric_vars <- sapply(fhs, is.numeric)
fhs_numeric <- fhs[, numeric_vars]

# Make sure TenYearCHD is included
fhs_numeric$TenYearCHD <- fhs$TenYearCHD  

# Random Forest for numeric features
library(randomForest)
rf_numeric <- randomForest(TenYearCHD ~ ., data = fhs_numeric, importance = TRUE)

# Extract importance
importance_numeric <- importance(rf_numeric)

# Create importance dataframe based on IncNodePurity
importance_numeric_df <- data.frame(
  Variable = rownames(importance_numeric),
  Importance = importance_numeric[, "IncNodePurity"]
)

# Sort by importance
importance_numeric_df <- importance_numeric_df[order(-importance_numeric_df$Importance), ]

# View results
print(importance_numeric_df)
             Variable Importance
sysBP           sysBP   71.20197
BMI               BMI   68.36096
totChol       totChol   64.73109
glucose       glucose   62.65319
age               age   61.73497
diaBP           diaBP   60.80589
heartRate   heartRate   49.53097
cigsPerDay cigsPerDay   28.65519

Table 2. Most important numeric features.

Using a Random Forest regression model, feature importance was evaluated for the numerical predictors of TenYearCHD. The results indicate that systolic blood pressure (sysBP), body mass index (BMI), and total cholesterol (totChol) are the most influential variables in predicting ten-year coronary heart disease risk. Other notable predictors include glucose, age, diastolic blood pressure (diaBP), and heart rate, while cigarettes per day (cigsPerDay) had the lowest relative importance among the numerical features considered.

To identify the most important categorical predictors of TenYearCHD, a Random Forest classification model was fit using only the categorical variables in the dataset. This feature selection step helps focus pairwise analyses on the most relevant categorical factors, improving the clarity and interpretability of the visualizations.

# Subset only categorical variables
factor_vars <- sapply(fhs, is.factor)
fhs_factor <- fhs[, factor_vars]


# Check for missing values in the fhs_factor dataset
#colSums(is.na(fhs_factor))

# Remove BPMeds_original if it exists
fhs_factor$BPMeds_original <- NULL

# Ensure the target variable TenYearCHD is a factor in the original data
fhs$TenYearCHD <- as.factor(fhs$TenYearCHD)

# Add TenYearCHD back to the factor dataset if it was dropped
fhs_factor$TenYearCHD <- fhs$TenYearCHD

# Train Random Forest classifier on categorical features
rf_factor <- randomForest(TenYearCHD ~ ., data = fhs_factor, importance = TRUE)

# Get importance values
importance_factor <- importance(rf_factor)

# Convert to a clean data frame
importance_factor_df <- data.frame(
  Variable = rownames(importance_factor),
  Importance = importance_factor[, "MeanDecreaseGini"]
)

# Sort by importance
importance_factor_df <- importance_factor_df[order(-importance_factor_df$Importance), ]

# Print
print(importance_factor_df)
                       Variable Importance
prevalentHyp       prevalentHyp  27.738833
diabetes               diabetes   8.203009
sex                         sex   7.644328
BPMeds                   BPMeds   5.466221
prevalentStroke prevalentStroke   4.588553
currentSmoker     currentSmoker   2.841952

Table 3. Most important categorical features.

Using a Random Forest classification model, feature importance was evaluated for the categorical predictors of TenYearCHD. The results show that prevalent hypertension (prevalentHyp) is by far the most influential categorical variable, followed by diabetes and sex. Other categorical features such as prevalent stroke, use of blood pressure medication (BPMeds), and current smoking status had lower relative importance in predicting ten-year coronary heart disease risk.

5 PAIRWISE RELATIONSHIPS

To explore the relationships between the most important numerical predictors of TenYearCHD, pairwise scatterplots are generated for the top five variables identified through Random Forest feature selection. These include systolic blood pressure (sysBP), body mass index (BMI), total cholesterol (totChol), glucose, and age. Analyzing these pairwise relationships helps to visually understand how these features correlate with each other and with the target variable, TenYearCHD.

# Select the top 5 most important numerical features
top_features <- fhs[, c("sysBP", "BMI", "totChol", "glucose", "age")]

# Compute the correlation matrix
cor_matrix <- cor(top_features)

# Print the correlation matrix with numbers
#print(cor_matrix)

# Create a clean correlation plot with numbers displayed inside the cells
corrplot(cor_matrix, 
         method = "color",         # Color-based visualization
         type = "full",            # Full matrix display
         col = colorRampPalette(c("red", "white", "blue"))(200), # Color gradient
         tl.cex = 0.8,             # Adjust label size
         number.cex = 0.8,         # Adjust correlation number size
         addCoef.col = "black",    # Add correlation coefficients in black
         title = "Correlation Matrix for Top 5 Numerical Features", 
         mar = c(0,0,1,0))         # Margins around the plot

Figure H. Correlation matrix for continuous features.

The scatterplot matrix below displays pairwise relationships between the five most important numerical features identified for predicting coronary heart disease. These features include systolic blood pressure (sysBP), body mass index (BMI), total cholesterol (totChol), glucose, and age. The scatterplots provide a visual representation of how these variables relate to one another, with each plot showcasing the distribution of data points between two features. The inclusion of bootstrapping reduces clutter by sampling 30% of the data, allowing for clearer insights into potential patterns and associations.

# Load necessary libraries
library(ggplot2)
library(gridExtra)

# Select the top 5 most important numerical features
top_features <- fhs[, c("sysBP", "BMI", "totChol", "glucose", "age")]

# Bootstrap the data (reduce size to 30% to declutter scatterplots)
set.seed(123)  # For reproducibility
top_features_sample <- top_features[sample(1:nrow(top_features), 0.3 * nrow(top_features)), ]

# Create the scatterplot matrix manually
pairs_list <- list()

# Loop over all pairs of features to create individual scatterplots
for(i in 1:(ncol(top_features_sample)-1)) {
  for(j in (i+1):ncol(top_features_sample)) {
    pairs_list[[paste(names(top_features_sample)[i], names(top_features_sample)[j], sep = "_")]] <- 
      ggplot(top_features_sample, aes_string(x = names(top_features_sample)[i], y = names(top_features_sample)[j])) +
      geom_point(alpha = 0.3, size = 0.5) + 
      labs(title = paste(names(top_features_sample)[i], "vs", names(top_features_sample)[j])) +
      theme_minimal() +
      theme(
        plot.title = element_text(size = 10, face = "bold", hjust = 0.5, vjust = 0),
        axis.title = element_text(size = 8),
        axis.text = element_text(size = 7)
      )
  }
}

# Arrange all scatterplots in a grid layout
grid.arrange(grobs = pairs_list, ncol = 5)  # Arrange in 5 columns, adjust ncol if needed

Figure I. Scatterplots of the five most important numeric features.

The pairwise scatterplots reveal several moderate linear relationships among the top features. Specifically, systolic blood pressure (sysBP) and body mass index (BMI) exhibit a moderate linear relationship, as do sysBP and total cholesterol (totChol), BMI and totChol, sysBP and age, and totChol and age. These relationships suggest that as one variable increases, there tends to be a corresponding increase in the other, indicating a moderate association between these key health factors.

# Set up the plotting area to have 2 rows and 3 columns
par(mfrow = c(2, 3))

# List of categorical variables to compare with TenYearCHD
categorical_vars <- c("sex", "currentSmoker", "BPMeds", "prevalentStroke", "prevalentHyp", "diabetes")

# Loop through each categorical variable
for (var in categorical_vars) {
  # Create mosaic plot for each variable compared with TenYearCHD
  mosaicplot(table(fhs[[var]], fhs$TenYearCHD), main = paste("TenYearCHD vs", var), 
             color = c("lightblue", "pink"), 
             xlab = paste(var), ylab = "TenYearCHD",
             las = 1)
  # Add labels for 1 = Yes, 0 = No
  text(x = 1, y = 1, labels = "1 = Yes", col = "black", pos = 4)
  text(x = 1, y = 2, labels = "0 = No", col = "black", pos = 4)
}

Figure J. Mosaic plots of relationships of categorical features.

The mosaic plots reveal that there is a significant association between TenYearCHD and all the categorical variables, except for “currentSmoker.” Specifically, variables like “sex,” “BPMeds,” “prevalentStroke,” “prevalentHyp,” and “diabetes” show clear patterns where the distribution of TenYearCHD differs across their levels. However, “currentSmoker” does not appear to exhibit a strong association with the outcome, suggesting that smoking status may not be a strong predictor of TenYearCHD in this dataset.

6 FEATURE ENGINEER

To enhance model performance and uncover meaningful patterns, feature engineering was applied to the Framingham Heart Study dataset. This process included creating new variables, such as clusters, interaction terms, and clinically relevant risk indicators, to better capture complex relationships with ten-year cardiovascular disease risk.

# Remove non-numeric columns (like factors and response variable)
fhs_numeric <- fhs[sapply(fhs, is.numeric)]
fhs_scaled <- scale(fhs_numeric)  # Standardize the features

set.seed(123)

# Standardize numeric variables
fhs_scaled <- scale(fhs[, sapply(fhs, is.numeric)])

# Compute WSS for k = 1 to 10
wss <- sapply(1:10, function(k){
  kmeans(fhs_scaled, centers = k, nstart = 10)$tot.withinss
})

# Plot Elbow Curve
plot(1:10, wss, type = "b", pch = 19, frame = FALSE,
     xlab = "Number of clusters K",
     ylab = "Total within-clusters sum of squares",
     main = "Elbow Method for Determining Optimal K")

# Run k-means with chosen number of clusters (e.g., 3)
kmeans_result <- kmeans(fhs_scaled, centers = 3, nstart = 25)

# Add cluster to the dataset
fhs$Cluster <- as.factor(kmeans_result$cluster)

# Run PCA just for plotting (2D representation)
pca_result <- prcomp(fhs_scaled)

# Create data frame for plotting
plot_data <- data.frame(PC1 = pca_result$x[,1],
                        PC2 = pca_result$x[,2],
                        Cluster = fhs$Cluster)

# Plot the clusters
library(ggplot2)
ggplot(plot_data, aes(x = PC1, y = PC2, color = Cluster)) +
  geom_point(size = 2, alpha = 0.8) +
  labs(title = "K-Means Clustering (Visualized via PCA)",
       x = "Principal Component 1",
       y = "Principal Component 2") +
  theme_minimal() +
  scale_color_brewer(palette = "Set1")

Figure K. Visualization of clusters.

The cluster analysis revealed three well-separated and distinct groups within the Framingham Heart dataset, suggesting meaningful underlying patterns in the data. These clusters may represent different cardiovascular risk profiles, which could help inform targeted prevention or intervention strategies.

7 METHODOLOGY: Predictive Modeling Approaches

Previous methods, such as regression models (including regularized versions like Lasso and Ridge) and SVMs, were used to address the formulated questions, excelling in linear relationships and smaller datasets. However, these models can struggle with non-linear data or high-dimensional feature spaces.

CART, Bagging, and Random Forests offer advantages by capturing complex, non-linear relationships without the need for linear assumptions. Random Forests, in particular, improve upon Bagging by constructing multiple decision trees and averaging their outputs, which helps to further reduce variance and avoid overfitting. While CART is interpretable, it may overfit without proper pruning. Bagging helps reduce variance by combining multiple models, and Random Forests further improve performance with ensemble learning. Each method has its strengths: regression models and SVMs excel at handling smaller, simpler datasets with linear or regularized relationships, while CART, Bagging, and Random Forests shine with larger datasets and more complex interactions. The trade-off between interpretability and performance is a key consideration when selecting the appropriate model for a given task.

8 CART REGRESSION

CART (Classification and Regression Trees) regression is a decision tree algorithm used to predict continuous outcomes, such as total cholesterol (totChol) in the Framingham Heart Study dataset. The model development follows these key components:

1.  Splitting Criterion: The tree recursively partitions the data by choosing splits that minimize the sum of squared errors (SSE) within the resulting nodes, aiming to create homogenous groups based on predictors like age, BMI, and blood pressure.
2.  Tree Structure and Stopping Rules: To prevent the tree from growing too complex, stopping rules such as minimum number of observations required to split a node (minsplit), minimum size of terminal nodes (minbucket), and maximum tree depth (maxdepth) are applied.
3.  Pruning: After the full tree is grown, cost-complexity pruning is used to avoid overfitting. This involves evaluating subtrees using cross-validation and selecting the optimal complexity parameter (cp) that balances model accuracy with simplicity.
4.  Interpretability: The final tree structure provides a transparent model showing how different combinations of predictors lead to different predicted cholesterol levels.

8.1 Initial Regression Tree

An initial regression tree was built to explore how various health and lifestyle factors predict total cholesterol levels in the Framingham dataset. By setting relatively flexible tree parameters, the model was able to identify key variables and potential nonlinear relationships through recursive partitioning.

# Set seed for reproducibility
set.seed(123)


# Split the data into training (70%) and testing (30%) sets
trainIndex <- createDataPartition(fhs$TenYearCHD, p = 0.7, list = FALSE)
train_data <- fhs[trainIndex, ]
test.data <- fhs[-trainIndex, ]


# Build the initial CART regression tree for predicting total cholesterol
tree_model <- rpart(
  totChol ~ ., 
  data = train_data,
  method = "anova",  # Regression tree
  control = rpart.control(
    minsplit = 10,         # Minimum number of observations required to attempt a split
    minbucket = 5,         # Minimum number of observations in terminal nodes
    cp = 0.002,             # Complexity parameter 
    maxdepth = 5           # Maximum depth of the tree
  )
)

# Visualize the unpruned regression tree
rpart.plot(tree_model, main = "Initial CART Regression Tree for Total Cholesterol")

Figure L. Initial regression tree before pruning.

9 Hyperparameter Tuning

To improve model performance and prevent overfitting, we performed hyperparameter tuning on the CART regression model using 10-fold cross-validation. Specifically, we tuned the complexity parameter (cp), which controls the trade-off between tree size and predictive accuracy.

# Set seed for reproducibility
set.seed(123)

# Build the initial regression tree with cross-validation
tree_model <- rpart(
  totChol ~ ., 
  data = train_data,
  method = "anova",  # Regression tree
  control = rpart.control(
    minsplit = 10,
    minbucket = 5,
    cp = 0.01,
    maxdepth = 5,
    xval = 10
  )
)

# View the complexity parameter table
pander(tree_model$cptable)
CP nsplit rel error xerror xstd
0.1619 0 1 1.001 0.04969
0.01281 1 0.8381 0.8389 0.04484
0.01185 2 0.8253 0.841 0.04547
0.01034 3 0.8134 0.8255 0.04547
0.01 4 0.8031 0.819 0.0453
# Plot the complexity parameter vs. error (xerror)
plotcp(tree_model)

# Choose the optimal cp based on xerror
min_xerror <- min(tree_model$cptable[, "xerror"])
best_cp_row <- which.min(tree_model$cptable[, "xerror"])
best_cp <- tree_model$cptable[best_cp_row, "CP"]

# Print the best cp value
cat("Best CP: ", round(best_cp, 4), "\n")
Best CP:  0.01 

Table 4. Cross-validation results for different complexity parameters.

Figure M. Plot illustrates the error curve.

Although the complexity plot shows an elbow at cp = 0.051, indicating a point where further splits begin to yield diminishing returns, cross-validation identified cp = 0.01 as the optimal value for minimizing prediction error while maintaining a reasonable model complexity. More specifically, the plot indicates that cp = 0.12 is best when using the 1-standard error (1-SE) rule.

9.1 Pruned Regression Tree: Best CP (1-SE)

This code selects complexity parameters (cp) for pruning the regression tree based on both the minimum cross-validation error and the 1-SE rule. It then prunes the tree and makes predictions on the test set, saving the R-squared and RMSE values for later comparison across regression models.

# Step 1: Identify the minimum xerror and its corresponding cp (for pruning with minimum error)
cp.table <- tree_model$cptable
min.xerror <- min(cp.table[, "xerror"])
min.cp.row <- which.min(cp.table[, "xerror"])
min.cp <- cp.table[min.cp.row, "CP"]

# Step 2: Compute the standard error of the minimum xerror
xerror.std <- cp.table[min.cp.row, "xstd"]
threshold <- min.xerror + xerror.std

# Step 3: Override best.cp manually to get a more complex tree
best.cp <- 0.012  # based on 1-SE rule, max CP for xerror < 1

# Step 4: Prune the tree using both min.cp and manually specified best.cp
pruned.tree.best.cp <- prune(tree_model, cp = best.cp)
pruned.tree.min.cp <- prune(tree_model, cp = min.cp)

# Step 5: Visualize the manually pruned tree
rpart.plot(pruned.tree.best.cp, 
           main = paste("Pruned Tree Best CP (1-SE): cp =", round(best.cp, 5)))

# Step 6: Make predictions on test data
pred.best.cp <- predict(pruned.tree.best.cp, newdata = test.data)
pred.min.cp  <- predict(pruned.tree.min.cp, newdata = test.data)

# Step 7: Save R-squared values
rsquared.bestcp <- cor(test.data$totChol, pred.best.cp)^2

# Save MSE for best.cp (1-SE rule)
rmse.bestcp <- sqrt(mean((pred.best.cp - test.data$totChol)^2))

Figure N. Pruned Tree Best CP using 1-SE Rule.

9.2 Pruned Regression Tree: Minimum CP

The following tree represents the pruned regression model using the complexity parameter (CP) that minimizes cross-validation error. This version of the tree (Minimum CP) aims to balance model complexity and predictive accuracy by fitting as closely as possible to the training data without overfitting.

# Visualize the pruned tree using the minimum CP value
rpart.plot(pruned.tree.min.cp,
           main = paste("Pruned Regression Tree (Min CP): cp =", round(min.cp, 4)),
           type = 2,                # Label all nodes with predicted values
           extra = 101,             # Show fitted values and % of observations at each node
           under = TRUE,            # Display the split condition underneath the node
           faclen = 0,              # Avoid abbreviating factor levels
           cex = 0.7,               # Shrink text for better fit
           fallen.leaves = TRUE,    # Better layout for the leaves at the bottom
           box.palette = "GnBu",    # Green-blue color theme for boxes
           shadow.col = "gray")     # Add shadows to boxes for depth

#r^2 for min.cp
rsquared.mincp  <- cor(test.data$totChol, pred.min.cp)^2
#Save MSE for min.cp (minimum xerror)
rmse.mincp <- sqrt(mean((pred.min.cp - test.data$totChol)^2))

Figure O. Pruned Regression Tree, Minimum CP.

The final CART regression model predicting total cholesterol identified sex and cigarettes per day as the most important predictors. This suggests that cholesterol levels in the Framingham dataset are most effectively segmented by gender and smoking behavior.

9.3 BAGGING CART Regression

To further improve predictive performance, we next apply Bagging (Bootstrap Aggregating) to CART regression. Bagging builds multiple trees on different bootstrap samples of the data and averages their predictions, helping to reduce variance and improve model stability compared to a single decision tree.

To optimize the Bagging CART regression model, we perform a systematic hyperparameter tuning process. We split the data into training and testing sets, set up 5-fold cross-validation, and explore different combinations of the number of bagged trees, complexity parameters (cp), and maximum tree depths to identify the settings that minimize out-of-bag (OOB) error.

# Split the data into training and testing sets
set.seed(123)
train.index <- createDataPartition(fhs$totChol, p = 0.8, list = FALSE)
train.data <- fhs[train.index, ]
test.data <- fhs[-train.index, ]

# Set up train control for cross-validation
ctrl <- trainControl(
  method = "cv",
  number = 5,
  verboseIter = TRUE
)

# Define parameter combinations to test
nbagg.values <- c(10, 25, 50)     # number of bagged trees
cp.values <- c(0.01, 0.05, 0.1)   # candidate cp values for rpart
maxdepth.values <- c(5, 10, 20)   # maximum depth of the candidate tree

# Create an empty data frame to store results
results <- data.frame()

# Model tuning loop
for (nbagg in nbagg.values) {
  for (cp in cp.values) {
    for (maxdepth in maxdepth.values) {
      set.seed(123)
      model <- bagging(
        totChol ~ .,
        data = train.data,
        nbagg = nbagg,
        coob = TRUE,
        trControl = ctrl,
        control = rpart.control(cp = cp, 
                                maxdepth = maxdepth)
      )
      # Get OOB error from each iteration
      oob.error <- model$err
      # Store results
      results <- rbind(results, 
                       data.frame(nbagg = nbagg,
                                  cp = cp,
                                  maxdepth = maxdepth,
                                  oob.error = oob.error))
    }
  }
}

# Find the best combination that yields the minimum out-of-bag error
best.params <- results[which.min(results$oob.error), ]
pander(best.params)
  nbagg cp maxdepth oob.error
19 50 0.01 5 39.09

Table 5. Best hyperparameter combination for Bagging CART regression, showing 50 trees, a complexity parameter (cp) of 0.01, and a maximum tree depth of 5, resulting in the lowest out-of-bag error (39.09).

The final Bagging CART model is fitted to the training data using the optimal hyperparameters from the tuning process. Predictions are then generated on the test set for later evaluation.

# Retrain the final model using the best parameters
set.seed(123)
final.bagging.model <- bagging(
  totChol ~ .,
  data = train.data,
  nbagg = best.params$nbagg,
  coob = TRUE,
  control = rpart.control(cp = best.params$cp, maxdepth = best.params$maxdepth)
)

# Make predictions on test set
pred.bagging <- predict(final.bagging.model, newdata = test.data)

# Calculate MSE and R-squared
rmse.bagging <- sqrt(mean((pred.bagging - test.data$totChol)^2))

ss.res <- sum((pred.bagging - test.data$totChol)^2)
ss.tot <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.bagging <- 1 - (ss.res / ss.tot)

In the next step, the importance of each predictor in the final Bagging model is evaluated. This analysis helps identify the features that have the most significant influence on predicting the target variable, offering insights into the model’s decision-making process.

 # Extract variable importance using caret's varImp function
var.imp <- varImp(final.bagging.model)

# Custom function to get variable importance from bagging model
get.bagging.importance <- function(model) {
  # Get all the trees from the bagging model
  trees <- model$mtrees
  
  # Initialize importance vector
  imp <- numeric(length(trees[[1]]$btree$variable.importance))
  names(imp) <- names(trees[[1]]$btree$variable.importance)
  
  # Sum importance across all trees
  for (tree in trees) {
    imp[names(tree$btree$variable.importance)] <- 
      imp[names(tree$btree$variable.importance)] + 
      tree$btree$variable.importance
  }
  
  # Average importance
  imp <- imp / length(trees)
  return(imp)
}

# Get variable importance from the final bagging model
bagging_importance <- get.bagging.importance(final.bagging.model)

# Display variable importance
#print(bagging_importance)

# Get importance
importance.scores <- get.bagging.importance(final.bagging.model)


# Set custom margins: bottom, left, top, right
par(mar = c(5, 8, 4, 2))  # increases left margin for labels


# Sort and plot
importance.scores <- sort(importance.scores, decreasing = TRUE)
barplot(importance.scores, horiz = TRUE, las = 1,
        main = "Variable Importance - Bagging (ipred)",
        xlab = "Importance Score")

Figure P. The bar chart displays the relative importance of each predictor variable in the final Bagging model, with the variables ordered by their contribution to the model’s predictions.

9.4 Random Forest Regression

Random Forest regression is a powerful ensemble learning technique that aggregates predictions from multiple decision trees to improve accuracy and robustness. In the process, hyperparameters such as the number of trees, the number of variables tried at each split, and the minimum node size play a crucial role in determining the model’s performance. The next step involves tuning these hyperparameters to optimize the Random Forest model for predicting total cholesterol (totChol) using cross-validation and evaluating the resulting performance.

Hyperparameter tuning for the Random Forest model involves testing different combinations of parameters, including mtry, ntree, and nodesize, to identify the optimal configuration based on RMSE performance.

# Hyperparameter Tuning for Random Forest
tune_grid <- expand.grid(
  mtry = c(2, 4, 6, 8),
  ntree = c(100, 200),
  nodesize = c(1, 5)
)

tune_results <- list()

for (i in 1:nrow(tune_grid)) {
  rf_temp <- randomForest(
    totChol ~ .,
    data = train.data,
    mtry = tune_grid$mtry[i],
    ntree = tune_grid$ntree[i],
    nodesize = tune_grid$nodesize[i]
  )
  preds <- predict(rf_temp, newdata = test.data)
  rmse <- sqrt(mean((test.data$totChol - preds)^2))
  tune_results[[i]] <- c(
    rmse = rmse,
    mtry = tune_grid$mtry[i],
    ntree = tune_grid$ntree[i],
    nodesize = tune_grid$nodesize[i]
  )
}

# Create data frame and find best parameters
tune_df <- as.data.frame(do.call(rbind, tune_results))
best_params <- tune_df[which.min(tune_df$rmse), ]

After tuning the Random Forest model, a separate Bagging model is built by setting the number of predictors considered at each split equal to the total number of available predictors. This approach emphasizes reducing variance by fully growing each tree and averaging their predictions for more stable results.

set.seed(123)

# Bagging model: mtry = total number of predictors
bagging_model <- randomForest(
  totChol ~ ., 
  data = train.data, 
  mtry = ncol(train.data) - 1,  # Exclude target variable
  importance = TRUE
)

# Predict on test data
bagging_pred <- predict(bagging_model, newdata = test.data)

# Performance: RMSE
bagging_rmse <- sqrt(mean((test.data$totChol - bagging_pred)^2))
#print(paste("Bagging RMSE:", round(bagging_rmse, 2)))

Using the best hyperparameters, the final Random Forest model is trained on the full training set. Predictions are made on the test data, and model performance is assessed through RMSE and R-squared values.

# Final Random Forest Model with Best Params
#-------------------------------------------
final_rf <- randomForest(
  totChol ~ ., 
  data = train.data,
  mtry = best_params$mtry,
  ntree = best_params$ntree,
  nodesize = best_params$nodesize,
  importance = TRUE
)

# Predict on test data
rf_pred <- predict(final_rf, newdata = test.data)

# Calculate MSE
rmse.rforest <- sqrt(mean((test.data$totChol - rf_pred)^2))

# Calculate R-squared
ss.res <- sum((test.data$totChol - rf_pred)^2)
ss.tot <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.rforest <- 1 - (ss.res / ss.tot)

print(final_rf)

Call:
 randomForest(formula = totChol ~ ., data = train.data, mtry = best_params$mtry,      ntree = best_params$ntree, nodesize = best_params$nodesize,      importance = TRUE) 
               Type of random forest: regression
                     Number of trees: 200
No. of variables tried at each split: 4

          Mean of squared residuals: 1501.703
                    % Var explained: 21.07

Table 6. Summary of the final Random Forest model, displaying the number of trees, selected hyperparameters, out-of-bag (OOB) error estimate, and variable importance measures.

Variable importance is evaluated for the final Random Forest model to identify the predictors that contribute most to the prediction of total cholesterol.

# Variable importance
importance(final_rf)
                   %IncMSE IncNodePurity
sex             15.1415565     168934.57
age             20.5671015     786271.21
currentSmoker    4.5871529      76396.55
cigsPerDay      11.5185748     329470.56
BPMeds           3.6936359      26805.30
prevalentStroke -0.9200363      11001.93
prevalentHyp     6.0569320      71403.75
diabetes        -1.7994060      31643.76
sysBP           10.0326148     692038.42
diaBP           11.4193775     639401.81
BMI              9.2685742     790493.00
heartRate        5.1588655     564034.29
glucose          0.4333843     626173.40
TenYearCHD       0.2739989      85944.16
Cluster         40.7558708     775366.72
varImpPlot(final_rf, main = "Random Forest Variable Importance")

Table 7. Variable importance table.

Figure Q. Variable importance plot.

The variable importance table presents two key metrics: %IncMSE, which measures the increase in mean squared error when a variable is randomly permuted, and IncNodePurity, which reflects the total decrease in node impurity due to splits on each variable. Higher values in either metric indicate greater influence on the model’s predictive accuracy.

The accompanying Random Forest Variable Importance Plot visually ranks the predictors based on these scores, providing an intuitive way to identify the most impactful features in the model’s performance.

The table of variable importance for the Random Forest model shows two key metrics: %IncMSE, which indicates how much the mean squared error increases when each variable is permuted, and IncNodePurity, which reflects the total decrease in node impurity due to each variable. To visualize these results, the variable importance plot ranks the features based on their impact, providing an intuitive view of which variables are most influential in predicting the target.

9.5 Comparison: Model Performance

In this analysis, various regression models are evaluated to predict total cholesterol (totChol) using the Framingham Heart Study dataset. The models’ performance is assessed through key metrics such as Root Mean Squared Error (RMSE) and R-squared, with the goal of determining which model most accurately captures the underlying patterns in the data.

#--- Assumes these values are already computed and stored ---
# mse.bestcp, mse.mincp
# rsquared.bestcp, rsquared.mincp
# mse.bagging, rsq.bagging
# mse.rforest, rsq.rforest

# Calculate LSE model (Stepwise AIC)
model.lse <- step(lm(totChol ~ ., data = train.data), direction = "both", trace = FALSE)
pred.lse <- predict(model.lse, newdata = test.data)

# Compute metrics for LSE model
rmse.lse <- sqrt(mean((test.data$totChol - pred.lse)^2))
ss.res.lse <- sum((test.data$totChol - pred.lse)^2)
ss.tot.lse <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.lse <- 1 - (ss.res.lse / ss.tot.lse)

#--- Create summary table ---
model_results <- data.frame(
  Model = c("Pruned Tree (min cp)", 
            "Pruned Tree (1-SE cp)", 
            "Bagging", 
            "Random Forest", 
            "Stepwise AIC (LSE)"),
  RMSE = c(rmse.mincp, 
          rmse.bestcp, 
          rmse.bagging, 
          rmse.rforest, 
          rmse.lse),
  R_squared = c(rsquared.mincp, 
                rsquared.bestcp, 
                rsq.bagging, 
                rsq.rforest, 
                rsq.lse)
)

# View comparison
print(model_results)
                  Model     RMSE R_squared
1  Pruned Tree (min cp) 37.80819 0.2045056
2 Pruned Tree (1-SE cp) 38.38551 0.1799623
3               Bagging 43.20233 0.1852816
4         Random Forest 42.67037 0.2052216
5    Stepwise AIC (LSE) 43.37807 0.1786397

Table 8. RMSE and R-squared values for the four tree models, with Stepwise AIC (LSE) as the baseline for comparison.

Random Forest outperformed the other regression models, achieving the highest R-squared and the lowest RMSE, indicating its superior ability to capture the relationships within the data. Pruned Tree (min cp) followed closely behind, showing competitive performance but slightly lower predictive accuracy compared to Random Forest. cleaned of redundancy but metric are different than my code

10 CART CLASSIFICATION

CART (Classification and Regression Trees) classification constructs a decision tree that splits the data into increasingly homogeneous groups based on a categorical outcome, such as the presence or absence of 10-year coronary heart disease (TenYearCHD) in the Framingham Heart Study dataset. This approach identifies the most important predictors and optimal split points to best classify individuals according to their risk of developing CHD within ten years. The key components for this analysis are listed below.

  1. Splitting Criterion: Uses measures like Gini impurity or entropy to choose the best variable and split point.
  2. Stopping Rules: Parameters such as minsplit, minbucket, and maxdepth control tree growth and prevent overfitting.
  3. Pruning: Reduces tree complexity using the complexity parameter (cp) and cross-validation to select an optimal subtree.
  4. Prediction and Evaluation: The final tree is used to predict outcomes on test data, typically evaluated using accuracy, sensitivity, specificity, and AUC.

10.1 Initial CART Classification

The CART classification model for predicting TenYearCHD is built using a subset of the Framingham Heart Study dataset. The tree is constructed with specific parameters such as limiting the depth of the tree and ensuring a minimum number of observations for splits. The resulting model represents the full, unpruned tree, which is then visualized to show the class probabilities and labels at each node, providing insights into how different variables contribute to the prediction of 10-year coronary heart disease risk.

# Set seed for reproducibility
set.seed(123)

# Split data into training (70%) and testing (30%) sets
train_index <- sample(1:nrow(fhs), size = 0.7 * nrow(fhs))
train_data <- fhs[train_index, ]
test_data <- fhs[-train_index, ]

# Build initial CART classification model (predicting TenYearCHD)
class_model <- rpart(
  TenYearCHD ~ ., 
  data = train_data,
  method = "class",  # Classification tree
  control = rpart.control(
    minsplit = 10,
    minbucket = 5,
    cp = 0.0001,
    maxdepth = 5
  )
)

# Visualize the classification tree
rpart.plot(class_model, 
           extra = 104,             # Show probabilities and class labels
           box.palette = "GnBu",    # Green to blue gradient
           branch.lty = 1, 
           shadow.col = "gray", 
           nn = TRUE,               # Show node numbers
           main = "Initial CART Classification Tree for TenYearCHD")

Figure R. Initial CART Decision Tree.

10.2 Tuning

This code evaluates the tuning of a classification tree model by examining the complexity parameter (cp) values from the model’s cost-complexity table (cptable). It identifies the cp that corresponds to the minimum cross-validation error (xerror) and the cp within the 1-standard error (1-SE) range, which helps select the simplest tree that still provides optimal performance. By tuning the cp, we aim to balance model complexity and predictive accuracy, with the 1-SE rule serving as a conservative approach to prevent overfitting. The output provides both the minimum cp and the cp within the 1-SE range, offering valuable insights into the trade-off between a more complex model and a simpler, potentially more generalizable one.

pander(class_model$cptable)
CP nsplit rel error xerror xstd
0.009461 0 1 1 0.04297
0.008734 4 0.9607 1.013 0.0432
0.004367 5 0.952 1.011 0.04316
0.001456 14 0.9061 1.035 0.04358
1e-04 17 0.9017 1.037 0.04361
min.row <- which.min(class_model$cptable[, "xerror"])
min.cp <- class_model$cptable[min.row, "CP"]
se <- class_model$cptable[min.row, "xstd"]
xerror.cutoff <- class_model$cptable[min.row, "xerror"] + se

# Now find the simplest (largest cp) tree within 1-SE range
cp.1SE <- max(class_model$cptable[class_model$cptable[, "xerror"] <= xerror.cutoff, "CP"])

cat("min.cp:", min.cp, "\n")
min.cp: 0.009461426 
cat("cp.1SE:", cp.1SE, "\n")
cp.1SE: 0.009461426 
# Check the number of terminal nodes (leaves) in both trees
#cat("Terminal nodes for pruned.tree.1SE:", length(pruned.tree.1SE$frame$var[pruned.tree.1SE$frame$var == "<leaf>"]), "\n")
#cat("Terminal nodes for pruned.tree.min:", length(pruned.tree.min$frame$var[pruned.tree.min$frame$var == "<leaf>"]), "\n")

# Compare splits between the two trees (structure)
#print(pruned.tree.1SE)
#print(pruned.tree.min)

# Print the structure of the pruned trees to inspect differences
#summary(pruned.tree.1SE)
#summary(pruned.tree.min)

Table 9. Complexity Parameter Table.

The minimum cross-validated error occurred at a complexity parameter (cp) value of 0.0102. Using the 1-SE rule, the simplest tree within one standard error of the minimum error also corresponds to the same cp value of 0.0102. This suggests that no further simplification is necessary, and the tree with cp = 0.0102 is both the most accurate and the most parsimonious choice.

The following plot displays the cross-validation results for the classification tree model, illustrating how the complexity parameter (cp) influences the model’s cross-validation error.

# Plot the cross-validation results
plotcp(class_model)

Figure S. CART Complexity Parameter (cp) Plot.

The cross-validation plot shows that the lowest error occurs at a tree size of 5, corresponding to a complexity parameter (cp) of 0.0062, suggesting that this tree size offers the best balance between model complexity and predictive performance.

10.3 CART Classification (1-SE Rule)

The pruned CART classification tree using the 1-SE rule is a method for simplifying the decision tree model by reducing its complexity while maintaining predictive performance. This approach involves selecting the smallest tree within one standard error of the minimum cross-validation error, ensuring a balance between model simplicity and accuracy. Using the plot, a cp of 0.0062 is used for 1-SE Rule.

# 1. Find the optimal cp value that minimizes cross-validated error
min.cp <- class_model$cptable[which.min(class_model$cptable[, "xerror"]), "CP"]

# 2. Manually specify the 1-SE cp value (you already found this earlier)
cp.1SE <- 0.0062

# 3. Prune the tree using the 1-SE cp value
pruned.tree.1SE <- prune(class_model, cp = cp.1SE)

# 4. Visualize the pruned tree (1-SE rule version)
rpart.plot(pruned.tree.1SE, 
           extra = 104,              # Show predicted class and probability
           box.palette = "GnBu",     # Green to blue color scheme
           branch.lty = 1, 
           shadow.col = "gray", 
           nn = TRUE, 
           main = "Pruned CART Classification Tree (1-SE Rule)")

# 5. Generate predictions for pruned tree (1-SE rule)
pred.prob.1SE <- predict(pruned.tree.1SE, test_data, type = "prob")[, 2]

# 6. Create ROC curve for pruned tree (1-SE rule)
roc.tree.1SE <- pROC::roc(test_data$TenYearCHD, pred.prob.1SE)

# 7. Access the AUC directly from the ROC object
auc.tree.1SE <- roc.tree.1SE$auc


# 8. Optionally, print the AUC
#print(paste("AUC for pruned tree (1-SE):", round(auc.tree.1SE, 4)))

Figure T. Pruned CART Classification tree using 1-SE Rule.

10.4 CART Classification Tree (Min CP)

The pruned CART classification tree is visualized using the minimum complexity parameter (cp) value, which helps in reducing model complexity while retaining predictive accuracy. This visualization highlights the class probabilities and the number of observations at each node, providing a clearer understanding of the model’s decision-making process.

# Prune the tree using the cp that gives minimum cross-validated error
pruned.tree.min <- prune(class_model, cp = min.cp)

# Visualize the pruned tree with the chosen minimum cp
rpart.plot(pruned.tree.min, 
           extra = 104,  # Displays more detailed information on nodes (class probabilities, number of observations)
           box.palette = "GnBu",  # Green to Blue color scheme for node boxes
           branch.lty = 1,  # Solid lines for branches
           shadow.col = "gray",  # Shadow color for node labels
           nn = TRUE,  # Add node numbers to the tree
           main = "Pruned CART Classification Tree (Min CP)"  # Title for the plot
)

Figure U. Pruned CART Classification tree using min CP.

The pruned CART classification tree using the minimum CP resulted in a single node, suggesting that this approach did not produce a meaningful or informative model.

10.5 BAGGING Cart Classification

The BAGGING (Bootstrap Aggregating) method is applied to the CART (Classification and Regression Trees) model to predict TenYearCHD, a binary outcome indicating the likelihood of cardiovascular disease within ten years. BAGGING combines multiple decision trees built on bootstrapped subsets of the data, reducing variance and improving model robustness. By averaging the predictions of individual trees, BAGGING helps mitigate overfitting, making it particularly useful for complex datasets like the Framingham Heart Study. The analysis explores the model’s performance through cross-validation, tuning hyperparameters, and evaluating the results based on RMSE and R-squared, ultimately identifying the optimal parameters for accurate predictions.

First, the BAGGING model is trained using the chosen training data and aggregate the predictions from each decision tree to make the final classification, ensuring that each model’s prediction contributes to the overall output.

# Set seed for reproducibility
set.seed(123)

# Ensure the target is a factor
fhs$TenYearCHD <- as.factor(fhs$TenYearCHD)

# Split data into training and testing sets
trainIndex <- createDataPartition(fhs$TenYearCHD, p = 0.7, list = FALSE)
trainData <- fhs[trainIndex, ]
testData <- fhs[-trainIndex, ]

# Create hyperparameter grid
hyper.grid <- expand.grid(
  nbagg = c(25, 50, 100),
  minsplit = c(5, 10, 20),
  maxdepth = c(5, 10, 20),
  cp = c(0.01, 0.001)
)

# Initialize tracking variables
results <- data.frame()
best.accuracy <- 0
best.params <- list()

# Loop through hyperparameter combinations
for(i in 1:nrow(hyper.grid)) {
  params <- hyper.grid[i, ]
  
  ctrl <- rpart.control(
    minsplit = params$minsplit,
    maxdepth = params$maxdepth,
    cp = params$cp
  )
  
  # Train Bagging model
  bag.model <- bagging(
    TenYearCHD ~ .,
    data = trainData,
    nbagg = params$nbagg,
    coob = TRUE,
    control = ctrl
  )
  
  # Predict on test set
  preds <- predict(bag.model, newdata = testData)
  
  # Accuracy
  cm <- confusionMatrix(preds, testData$TenYearCHD)
  accuracy <- cm$overall["Accuracy"]
  
  # Store results
  results <- rbind(results, data.frame(
    nbagg = params$nbagg,
    minsplit = params$minsplit,
    maxdepth = params$maxdepth,
    cp = params$cp,
    Accuracy = accuracy
  ))
  
  # Track best result
  if(accuracy > best.accuracy) {
    best.accuracy <- accuracy
    best.params <- params
  }
}

# Display best parameters
cat("Best BAGGING hyperparameters:\n")
Best BAGGING hyperparameters:
pander(best.params)
  nbagg minsplit maxdepth cp
32 50 10 5 0.001
# Optional: View full results
# pander(results)

Table 10. Train final BAGGING Classification model with best hyperparameters

The output shows the optimal hyperparameters for the BAGGING model, with 100 trees (nbagg), a minimum split of 20 observations (minsplit), a maximum depth of 20 for each tree (maxdepth), and a complexity parameter (cp) of 0.001.

Next the final BAGGING model is trained using the best hyperparameters identified during tuning. The model is then used to predict the test data, and the results are evaluated using a labeled confusion matrix to assess the classification accuracy for predicting TenYearCHD.

# Set rpart control using best parameters from tuning
best.control <- rpart.control(
  minsplit = best.params$minsplit,
  maxdepth = best.params$maxdepth,
  cp = best.params$cp
)

# Train the final Bagging model on training data
final.bag.model <- bagging(
  TenYearCHD ~ .,
  data = trainData,
  nbagg = best.params$nbagg,
  coob = TRUE,
  control = best.control
)

# Generate predictions (probabilities) for the Bagging model
pred.bag.prob <- predict(final.bag.model, newdata = testData, type = "prob")[, 2]  # [ ,2] selects the probability for the positive class (1)

# Compute ROC for Bagging model
roc.bagging <- roc(testData$TenYearCHD, pred.bag.prob)

# Compute AUC for Bagging model
auc.bagging <- roc.bagging$auc

# Plot ROC curve for the Bagging model
#plot(roc.bagging, main = "ROC Curve - Bagging", col = "blue", lwd = 2)

# Now proceed with comparing other models and plotting as needed

10.6 Random Forest Classification

Random Forest classification is an ensemble learning method that builds multiple decision trees and aggregates their predictions to improve classification accuracy. Each tree is trained on a random subset of the data with random feature selection at each split, which helps reduce overfitting and increases the model’s robustness. The final prediction is determined by the majority vote of the individual trees, making Random Forest a powerful tool for classification tasks, especially when dealing with complex and high-dimensional data.

Hyperparameter tuning involves selecting the optimal settings for a model’s parameters, such as the number of trees, the maximum depth of each tree, and the minimum number of samples required for a split, to enhance its performance and accuracy.

set.seed(123)

# Drop BPMeds_original from both sets
train.data <- train.data[, !colnames(train.data) %in% "BPMeds_original"]
test.data <- test.data[, !colnames(test.data) %in% "BPMeds_original"]

# Ensure same columns and column order
test.data <- test.data[, names(train.data)]

# Ensure all factor levels are aligned
for (col in names(train.data)) {
  if (is.factor(train.data[[col]])) {
    test.data[[col]] <- factor(test.data[[col]], levels = levels(train.data[[col]]))
  }
}

# Two-way split: training/testing
train.idx <- createDataPartition(fhs$TenYearCHD, p = 0.7, list = FALSE)
train.data <- fhs[train.idx, ]
test.data <- fhs[-train.idx, ]

test.data <- test.data[, names(train.data)]

# Ensure outcome is factor with levels "0", "1"
train.data$TenYearCHD <- as.factor(train.data$TenYearCHD)
test.data$TenYearCHD <- as.factor(test.data$TenYearCHD)
# Ensure 'BPMeds_original' is not in the data
train.data <- train.data[, !colnames(train.data) %in% "BPMeds_original"]

# Cross-validation setup
k <- 5
train.size <- nrow(train.data)
fold.size <- floor(train.size / k)

# Hyperparameter grid
tune.grid <- expand.grid(
  mtry = c(2, 3, 4, 5),
  ntree = c(100, 300, 500),
  nodesize = c(1, 3, 5, 10),
  maxnodes = c(5, 10, 20, NA)
)

# Ensure valid mtry values before starting
p <- ncol(train.data) - 1  # Number of predictors
tune.grid <- subset(tune.grid, mtry >= 1 & mtry <= p & nodesize < maxnodes)

# Initialize results storage
results <- data.frame()
best.auc <- 0
best.hyp.params <- NULL

# Grid search with cross-validation
for (i in 1:nrow(tune.grid)) {
  current.tune.params <- tune.grid[i, ]
  cv.auc <- numeric(k)  # Preallocate AUC vector for folds
  
  for (j in 1:k) {
    # Define training and validation sets
    cv.id <- ((j - 1) * fold.size + 1):(j * fold.size)
    cv.train <- train.data[-cv.id, ]
    cv.valid <- train.data[cv.id, ]
    
    # Train the model
    rf.cv <- randomForest(
      TenYearCHD ~ .,
      data = cv.train,
      mtry = current.tune.params$mtry,
      ntree = current.tune.params$ntree,
      nodesize = current.tune.params$nodesize,
      maxnodes = current.tune.params$maxnodes
    )
    
    # Predict probabilities for positive class '1'
    prob.cv <- predict(rf.cv, newdata = cv.valid, type = "prob")[, "1"]
    
    # Compute AUC using pROC
    roc_obj <- pROC::roc(
      response = cv.valid$TenYearCHD,
      predictor = prob.cv,
      direction = "<"
    )
    cv.auc[j] <- pROC::auc(roc_obj)
  }
  
  # Compute average AUC over folds
  avg.auc <- mean(cv.auc)
  
  # Store results
  results <- rbind(results, data.frame(
    mtry = current.tune.params$mtry,
    ntree = current.tune.params$ntree,
    nodesize = current.tune.params$nodesize,
    maxnodes = current.tune.params$maxnodes,
    auc = avg.auc
  ))
  
  # Update best if current is better
  if (avg.auc > best.auc) {
    best.auc <- avg.auc
    best.hyp.params <- current.tune.params
  }
}

# Display best parameters
pander(data.frame(cbind(best.hyp.params, best.auc)))
  mtry ntree nodesize maxnodes best.auc
143 4 500 10 20 0.6965

Table 11. Random Forest Hyperparameter Tuning Results Table

The output indicates the optimal hyperparameters for the Random Forest model, with mtry set to 4, ntree set to 500, nodesize set to 10, and maxnodes set to 20. These settings resulted in a best AUC of 0.6965, which reflects the model’s performance in distinguishing between the classes.

To finalize the Random Forest model, the best hyperparameters identified from tuning are used to train the model on the full training dataset. Predictions for class probabilities of the positive class (“1”) are then made for the test data. In the subsequent steps, the model’s performance will be assessed using the ROC curve and AUC, which will be presented during the comparison section.

# Train the final Random Forest model using best parameters
final.rf.cls <- randomForest(
  TenYearCHD ~ .,
  data = train.data,
  ntree = best.hyp.params$ntree,
  mtry = best.hyp.params$mtry,
  nodesize = best.hyp.params$nodesize,
  maxnodes = best.hyp.params$maxnodes,
  importance = TRUE
)

# Predict class probabilities for the positive class "1"
pred.rf.prob <- predict(final.rf.cls, test.data, type = "prob")[, "1"]

# Ensure response is a factor with correct levels
test.response <- factor(test.data$TenYearCHD, levels = c(0, 1))

# Compute ROC and AUC
rf.roc <- pROC::roc(response = test.response, predictor = pred.rf.prob, levels = c("0", "1"), direction = "<")
rf.auc <- pROC::auc(rf.roc)

# Display AUC
#print(rf.auc)

The importance of each predictor variable in the final Random Forest model is assessed using two metrics: Mean Decrease in Accuracy and Mean Decrease in Gini. These measures are extracted and visualized to highlight the most influential variables, providing insights into which features contribute the most to the model’s predictive power. The results are displayed in a bar plot that separates the importance scores for each metric.

# Extract importance measures
importance_vals <- importance(final.rf.cls)
importance_df <- data.frame(
  Variable = rownames(importance_vals),
  MeanDecreaseAccuracy = importance_vals[, "MeanDecreaseAccuracy"],
  MeanDecreaseGini = importance_vals[, "MeanDecreaseGini"]
)


# Pivot for plotting
importance_long <- pivot_longer(
  importance_df,
  cols = c(MeanDecreaseAccuracy, MeanDecreaseGini),
  names_to = "Metric",
  values_to = "Importance"
)

# Plot
ggplot(importance_long, aes(x = reorder(Variable, Importance), y = Importance, fill = Metric)) +
  geom_bar(stat = "identity", position = "dodge") +
  coord_flip() +
  facet_wrap(~ Metric, scales = "free_x") +
  labs(title = "Variable Importance (Random Forest)",
       x = "Variable",
       y = "Importance Score") +
  theme_minimal() +
  theme(legend.position = "none")

Figure V. Random Forest Feature Importance Visualization

10.7 Comparison

To evaluate and compare the predictive performance of the pruned CART models, logistic regression, Bagging, and Random Forest, we generated ROC (Receiver Operating Characteristic) curves using the test dataset. These curves illustrate the trade-off between sensitivity and specificity across all possible classification thresholds, providing a visual comparison of model performance across different methods.

# Fit the Logistic Regression Model
logit_model <- glm(TenYearCHD ~ ., data = train.data, family = binomial)

# Predict probabilities for the test set
pred.logit.prob <- predict(logit_model, newdata = test.data, type = "response")

# Ensure response is a factor with correct levels
test.response <- factor(test.data$TenYearCHD, levels = c(0, 1))

# Compute ROC and AUC for Logistic Regression
logit.roc <- roc(response = test.response, predictor = pred.logit.prob, levels = c("0", "1"), direction = "<")
logit.auc <- pROC::auc(logit.roc)


# Recompute prediction, ensure it's aligned and un-named
pred.prob.1SE <- predict(pruned.tree.1SE, newdata = test.data, type = "prob")[, 2]
pred.prob.1SE <- as.numeric(pred.prob.1SE)  # removes any row names

# ROC for each model (ensure the model probabilities are defined first)
roc.tree.1SE <- roc(test.data$TenYearCHD, pred.prob.1SE)

pred.prob.min <- predict(pruned.tree.min, newdata = test.data, type = "prob")[, 2]
pred.prob.min <- as.numeric(pred.prob.min)

roc.tree.min <- roc(test.data$TenYearCHD, pred.prob.min)
roc.bagging <- roc(test.data$TenYearCHD, pred.bag.prob)
roc.rf <- roc(test.data$TenYearCHD, pred.rf.prob)
roc.logit <- roc(test.data$TenYearCHD, pred.logit.prob)

# AUC values
auc.tree.1SE <- pROC::auc(roc.tree.1SE)
auc.tree.min <- pROC::auc(roc.tree.min)
auc.bagging <- pROC::auc(roc.bagging)
auc.rf <- pROC::auc(roc.rf)
auc.logit <- pROC::auc(roc.logit)

# Plot ROC curves for comparison
par(mar = c(5, 5, 4, 1))
plot(1 - roc.logit$specificities, roc.logit$sensitivities, 
     type = "l", col = "darkorange", lwd = 2, 
     xlab = "1 - Specificity", ylab = "Sensitivity", 
     main = "ROC Curve Comparison")

lines(1 - roc.tree.1SE$specificities, roc.tree.1SE$sensitivities, col = "gold", lwd = 2)
lines(1 - roc.tree.min$specificities, roc.tree.min$sensitivities, col = "purple", lwd = 2)
lines(1 - roc.bagging$specificities, roc.bagging$sensitivities, col = "blue", lwd = 2)
lines(1 - roc.rf$specificities, roc.rf$sensitivities, col = "forestgreen", lwd = 2)

# Add a legend for clarity
legend("bottomright",
       legend = c("Logistic", "CART 1SE", "CART Min", "Random Forest", "Bagging"),
       col = c("darkorange", "gold", "purple", "blue", "forestgreen"),
       lty = c(1, 1, 1, 1, 1),
       lwd = 2,
       bty = "n",
       cex = 0.6)

### === Annotate with AUCs === ###
# Annotating the plot with the AUC values
text(0.75, 0.48, paste("Logistic AUC: ", round(auc.logit, 4)), cex = 0.7)
text(0.75, 0.42, paste("CART 1SE AUC: ", round(auc.tree.1SE, 4)), cex = 0.7)
text(0.75, 0.36, paste("CART Min AUC: ", round(auc.tree.min, 4)), cex = 0.7)
text(0.75, 0.30, paste("Random Forest AUC: ", round(auc.rf, 4)), cex = 0.7)
text(0.75, 0.24, paste("Bagging AUC: ", round(auc.bagging, 4)), cex = 0.7)

Figure W. ROC Curves for Classification Models.

Receiver Operating Characteristic (ROC) Curves for Multiple Predictive Models: The plot compares the ROC curves for Logistic Regression, CART with 1-SE pruning, CART with Minimum Cost-Complexity Pruning, Bagging, and Random Forest. AUC values for each model are annotated, highlighting their classification performance. Among the models evaluated, Logistic Regression achieved the highest AUC, demonstrating superior accuracy in distinguishing between individuals who will and will not develop coronary heart disease over a ten-year period. This suggests that, in this scenario, Logistic Regression provides a more reliable risk prediction tool compared to the other models.

10.8 Optimal Cutoff

The optimal cutoff represents the threshold probability at which the classification model balances sensitivity and specificity, maximizing the overall performance of the model. By using this cutoff, we ensure that predictions are made with the most effective trade-off between correctly identifying positive cases and minimizing false positives, which is crucial for making reliable decisions in practical applications such as healthcare or finance.

# Find the optimal cutoff using Youden's J statistic
coords.optimal <- coords(roc.logit, x = "best", best.method = "youden", ret = c("threshold", "sensitivity", "specificity"))

# Print the optimal cutoff and associated sensitivity/specificity
print(coords.optimal)
  threshold sensitivity specificity
1 0.1563719   0.6321244    0.703154

Table 12. The optimal cutoff.

The threshold value of 0.1563719 is the optimal cutoff. It is chosen to balance the sensitivity and specificity. If you are using this threshold, the model will classify a person as at risk for coronary heart disease if their predicted probability is greater than 15.6%. A sensitivity of 63.2% indicates that the model is fairly good at identifying individuals who will develop coronary heart disease. However, it misses about 36.8% of the true positives (false negatives). On the other hand, the specificity of 70.3% indicates that the model does a better job of correctly identifying individuals who will not develop the disease, though there is still some room for improvement (about 29.7% false positives).

11 CONCLUSION

The regression models evaluated, including Pruned Trees (both 1-SE and Minimum Cost-Complexity), Bagging, and Random Forest, provided valuable insights into predicting total cholesterol levels (totChol). Among these, the Random Forest model demonstrated the best performance with the lowest RMSE and highest R-squared value. The Pruned Tree (Minimum Cost-Complexity) model, while still performing well, showed a higher RMSE, suggesting that its predictions were less accurate. Given the nature of the task—predicting a continuous variable such as cholesterol levels—it is recommended to utilize Random Forest as the primary model for future predictions due to its higher accuracy and robustness. However, for interpretability and simplicity, Pruned Trees may also be considered when a more transparent model is needed.

For classification, the comparison of models such as Logistic Regression, CART (1-SE and Min), Bagging, and Random Forest for predicting coronary heart disease (TenYearCHD) revealed that Logistic Regression outperformed all other models, achieving the highest AUC. This suggests that Logistic Regression is particularly effective at distinguishing between individuals who will and will not develop coronary heart disease in the next ten years. While Random Forest and Bagging showed strong performance, Logistic Regression provided a more reliable and simpler model for this task. Therefore, it is recommended to prioritize Logistic Regression for future use in clinical applications where understanding and explaining the risk of coronary heart disease is critical. For improved performance in more complex datasets or non-linear relationships, Random Forest and Bagging can be considered as alternative models for further exploration.

LS0tCnRpdGxlOiAiTW9kZWxpbmcgQ29yb25hcnkgSGVhcnQgRGlzZWFzZSBSaXNrIGFuZCBDaG9sZXN0ZXJvbCBMZXZlbHM6IEEgTWFjaGluZSBMZWFybmluZyBBcHByb2FjaCB3aXRoIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IgphdXRob3I6ICJKb2FubmUgTXVzYSIKZGF0ZTogIkFwcmlsIDIwMjUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICB3b3JkX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGtlZXBfbWQ6IHllcwogIHBkZl9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgZmlnX3dpZHRoOiAzCiAgICBmaWdfaGVpZ2h0OiAzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBgez1odG1sfQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLwoKaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8KICBmb250LXNpemU6IDIycHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsKfQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IG5hdnk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Cmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovCiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwp9CgpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgoKaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZTEwNzEiKQpsaWJyYXJ5KGUxMDcxKQp9CmlmICghcmVxdWlyZSgibW1lbG4iKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJtbWVsbiIpCmxpYnJhcnkobW1lbG4pCn0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpCmxpYnJhcnkoTUFTUykKfQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKbGlicmFyeShwbG90bHkpCn0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKbGlicmFyeShjYXJldCkKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCmxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCn0KaWYgKCFyZXF1aXJlKCJycGFydCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikKbGlicmFyeShycGFydCkKfQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikKbGlicmFyeShycGFydC5wbG90KQp9CmlmICghcmVxdWlyZSgicFJPQyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQpsaWJyYXJ5KHBST0MpCn0KCmlmICghcmVxdWlyZSgiY29ycnBsb3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpCmxpYnJhcnkoY29ycnBsb3QpCn0KCmlmICghcmVxdWlyZSgiZ2xtbmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikKbGlicmFyeShnbG1uZXQpCn0KCmlmICghcmVxdWlyZSgiTWV0cmljcyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIk1ldHJpY3MiKQpsaWJyYXJ5KE1ldHJpY3MpCn0KCmlmICghcmVxdWlyZSgibWljZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQpsaWJyYXJ5KG1pY2UpCn0KCmlmICghcmVxdWlyZSgiaXByZWQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJpcHJlZCIpCmxpYnJhcnkobWljZSkKfQoKCgojIwprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKYGBgCgoKIyBJTlRST0RVQ1RJT046IEZSQU1JTkdIQU0gREFUQSBTVUJTRVQKClRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5LCBpbml0aWF0ZWQgaW4gMTk0OCwgd2FzIGRlc2lnbmVkIHRvIGlkZW50aWZ5IHRoZSBmYWN0b3JzIGFuZCBjaGFyYWN0ZXJpc3RpY3MgdGhhdCBjb250cmlidXRlIHRvIHRoZSBkZXZlbG9wbWVudCBvZiBjYXJkaW92YXNjdWxhciBkaXNlYXNlLiBCYXNlZCBpbiBNYXNzYWNodXNldHRzLCB0aGUgc3R1ZHkgaGFzIGZvbGxvd2VkIG11bHRpcGxlIGdlbmVyYXRpb25zIG9mIHBhcnRpY2lwYW50cyB1c2luZyBhIGxvbmdpdHVkaW5hbCBkZXNpZ24gdGhhdCBpbmNsdWRlcyByZWd1bGFyIGV4YW1pbmF0aW9ucyBhbmQgaGVhbHRoIGFzc2Vzc21lbnRzLiBQYXJ0aWNpcGFudHMgdW5kZXJnbyBjb21wcmVoZW5zaXZlIG1lZGljYWwgZXZhbHVhdGlvbnMsIGxhYm9yYXRvcnkgdGVzdGluZywgYW5kIGxpZmVzdHlsZSBhc3Nlc3NtZW50cy4gVGhlIHN0dWR5IGNhcHR1cmVzIGEgd2lkZSByYW5nZSBvZiByaXNrIGZhY3RvcnMsIGluY2x1ZGluZyBib3RoIGJlaGF2aW9yYWwgYW5kIGJpb2xvZ2ljYWwgY29tcG9uZW50cy4KClRoaXMgYW5hbHlzaXMgZm9jdXNlcyBvbiBhIHN1YnNldCBvZiBkYXRhIGZyb20gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuIFNwZWNpZmljYWxseSwgaXQgaW52ZXN0aWdhdGVzIGhvdyBzZWxlY3RlZCBiaW9sb2dpY2FsIGFuZCBiZWhhdmlvcmFsIHJpc2sgZmFjdG9ycyBwcmVkaWN0IHRoZSBsaWtlbGlob29kIG9mIGRldmVsb3BpbmcgY29yb25hcnkgaGVhcnQgZGlzZWFzZSAoQ0hEKSB3aXRoaW4gYSB0ZW4teWVhciBwZXJpb2QuCgoKIyMgRGVzY3JpcHRpb24gb2YgRGF0YQoKVGhlIG9yaWdpbmFsIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgaW5jbHVkZXMgZGF0YSBmcm9tIGFwcHJveGltYXRlbHkgMTUsMDAwIHBhcnRpY2lwYW50cyBhbmQgb3ZlciAxLDAwMCB2YXJpYWJsZXMuIEZvciB0aGlzIGFuYWx5c2lzLCBhIHN1YnNldCBvZiB0aGUgZGF0YSBpcyB1c2VkLCBjb250YWluaW5nIDQsMjQwIG9ic2VydmF0aW9ucyBhbmQgMTYgdmFyaWFibGVzLgoKVGhpcyBhbmFseXNpcyBmb2N1c2VzIHNwZWNpZmljYWxseSBvbiBiaW9sb2dpY2FsIGZlYXR1cmVzIHRoYXQgZGlyZWN0bHkgY29udHJpYnV0ZSB0byBoZWFsdGggb3V0Y29tZXMuIFByaW9yIGFuYWx5c2lzIChodHRwczovL2ptdXNhMy5naXRodWIuaW8vam11c2EvcHJvamVjdC9Qcm9qZWN0XzIuaHRtbCkgaW5kaWNhdGVkIHRoYXQgZWR1Y2F0aW9uIHdhcyBub3QgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igb2YgZWl0aGVyIFRlbi1ZZWFyIENvcm9uYXJ5IEhlYXJ0IERpc2Vhc2UgKENIRCkgcmlzayBvciB0b3RhbCBjaG9sZXN0ZXJvbCBsZXZlbHMsIHN1cHBvcnRpbmcgaXRzIHJlbW92YWwgZnJvbSB0aGUgZmVhdHVyZSBzZXQuIEFzIHN1Y2gsIHRoZSB2YXJpYWJsZSDigJxFZHVjYXRpb27igJ0gaGFzIGJlZW4gZXhjbHVkZWQgZnJvbSB0aGUgZGF0YXNldC4KClRoZSBvcmlnaW5hbCBmZWF0dXJlICJtYWxlIiBpcyBjaGFuZ2VkIHRvICJzZXgiIGZvciBlYXNlIG9mIGNvbXByZWhlbnNpb24uIEFmdGVyIGV4Y2x1ZGluZyDigJxFZHVjYXRpb24s4oCdIHRoZSBkYXRhc2V0IGZvciB0aGlzIGFuYWx5c2lzIGNvbnNpc3RzIG9mIDE1IHByZWRpY3RvciB2YXJpYWJsZXM6CgoJ4oCiCTcgZmFjdG9yIHZhcmlhYmxlcyAoY2F0ZWdvcmljYWwpCgnigKIJOCBudW1lcmljIHZhcmlhYmxlcyAoY29udGludW91cykKClRoZSBuYW1lcyBhbmQgZGVmaW5pdGlvbnMgb2YgdGhlc2UgdmFyaWFibGVzIGFyZSBwcm92aWRlZCBiZWxvdy4KCjEuIHNleCAoZmFjdG9yKTogdGhlIHNleCBvZiB0aGUgb2JzZXJ2YXRpb25zOiBmZW1hbGUgKDApLCBtYWxlICgxKS4KMi4gYWdlIChudW1lcmljYWwpOiBBZ2UgYXQgdGhlIHRpbWUgb2YgbWVkaWNhbCBleGFtaW5hdGlvbiBpbiB5ZWFycy4KMy4gY3VycmVudFNtb2tlciAoZmFjdG9yKTogQ3VycmVudCBjaWdhcmV0dGUgc21va2luZyBhdCB0aGUgdGltZSBvZiBleGFtaW5hdGlvbnMgCjQuIGNpZ3NQZXJEYXkgKG51bWVyaWNhbCk6IE51bWJlciBvZiBjaWdhcmV0dGVzIHNtb2tlZCBlYWNoIGRheSAKNS4gQlBtZWRzIChmYWN0b3IpOiBVc2Ugb2YgQW50aS1oeXBlcnRlbnNpdmUgbWVkaWNhdGlvbiBhdCBleGFtIAo2LiBwcmV2YWxlbnRTdHJva2UgKGZhY3Rvcik6IFByZXZhbGVudCBTdHJva2UgKDAgPSBmcmVlIG9mIGRpc2Vhc2UpIAo3LiBwcmV2YWxlbnRIeXAgKGZhY3Rvcik6IFByZXZhbGVudCBIeXBlcnRlbnNpdmUuIFN1YmplY3Qgd2FzIGRlZmluZWQgYXMgaHlwZXJ0ZW5zaXZlIGlmIHRyZWF0ZWQgCjguIGRpYWJldGVzIChmYWN0b3IpOiBEaWFiZXRpYyBhY2NvcmRpbmcgdG8gY3JpdGVyaWEgb2YgZmlyc3QgZXhhbSB0cmVhdGVkIAo5LiB0b3RDaG9sIChudW1lcmljYWwpOiBUb3RhbCBjaG9sZXN0ZXJvbCAobWcvZEwpIAoxMC4gc3lzQlAgKG51bWVyaWNhbCk6IFN5c3RvbGljIEJsb29kIFByZXNzdXJlIChtbUhnKSAKMTEuIGRpYUJQIChudW1lcmljYWwpOiBEaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tSGcpIAoxMi4gQk1JIChudW1lcmljYWwpOiBCb2R5IE1hc3MgSW5kZXgsIHdlaWdodCAoa2cpL2hlaWdodCAobSleMiAKMTMuIGhlYXJ0UmF0ZSAobnVtZXJpY2FsKTogSGVhcnQgcmF0ZSAoYmVhdHMvbWludXRlKSAKMTQuIGdsdWNvc2UgKG51bWVyaWNhbCk6IEJsb29kIGdsdWNvc2UgbGV2ZWwgKG1nL2RMKSAKMTUuIFRlblllYXJDSEQgKGZhY3RvciwgcmVzcG9uc2UgdmFyaWFibGUpOiBUaGUgMTAgeWVhciByaXNrIG9mIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UoQ0hEKS4KClRoZSBkYXRhIHNldCBoYXMgbWlzc2luZyB2YWx1ZXMgZm9yIHNpeCB2YXJpYWJsZXM6IGNpZ1BlckRheSwgQlBNZWRzLCB0b3RDaG9sLCBCTUksIGhlYXJ0UmF0ZSwgYW5kIGdsdWNvc2UuIAoKYGBge3J9CgpmaHMgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qbXVzYTMvam11c2EvcmVmcy9oZWFkcy9tYWluL0ZyYW1pbmdoYW1IZWFydFN0dWR5LmNzdiIpCgpmaHMgPC0gZmhzICU+JSBkcGx5cjo6c2VsZWN0KC1lZHVjYXRpb24pCgojIGNoYW5nZSAnbWFsZScgdG8gJ3NleCcgd2hlcmUgMSA9IG1hbGUgYW5kIDAgPSBmZW1hbGUKZmhzIDwtIGZocyAlPiUKICByZW5hbWUoc2V4ID0gbWFsZSkKZmhzJHNleCA8LSBmYWN0b3IoZmhzJHNleCkKZmhzJEJQTWVkcyA8LSBmYWN0b3IoZmhzJEJQTWVkcykKZmhzJGN1cnJlbnRTbW9rZXIgPC0gZmFjdG9yKGZocyRjdXJyZW50U21va2VyKQpmaHMkcHJldmFsZW50U3Ryb2tlIDwtIGZhY3RvcihmaHMkcHJldmFsZW50U3Ryb2tlKQpmaHMkcHJldmFsZW50SHlwIDwtIGZhY3RvcihmaHMkcHJldmFsZW50SHlwKQpmaHMkZGlhYmV0ZXMgPC0gZmFjdG9yKGZocyRkaWFiZXRlcykKCnN1bW1hcnkoZmhzKQojaGVhZChmaHMpCmBgYAoKVGFibGUgMS4gU3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgMTUgZmVhdHVyZXMgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgc3Vic2V0LiBTaXggb2YgdGhlIHZhcmlhYmxlcyBoYXZlIG1pc3NpbmcgdmFsdWVzLiAKCgoKIyBFREEKCiMjIFNpbmdsZSBGZWF0dXJlcwoKQWxsIG9mIHRoZSBmZWF0dXJlcyBpbiB0aGlzIGRhdGEgc2V0IGFyZSB0eXBlZCBhcyBudW1lcmljYWwuIEluIHR1cm4sIGluZGljYXRvciB2YXJpYWJsZXMgYXJlIGNoYW5nZWQgdG8gZmFjdG9yLiBGb3IgdmlzdWFsaXphdGlvbiwgIm1hbGUiIGlzIGNoYW5nZWQgdG8gInNleCIuIFRoZSBpbmRpY2F0b3IgdmFsdWVzIGZvciB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdlcmUgY2hhbmdlZCB0byBkZXNjcmlwdGl2ZSB3b3JkcyBvciBwaHJhc2VzIHRvIGVuaGFuY2UgcmVhZGFiaWxpdHkgb2YgdGhlIGdyYXBocy4gCgoKYGBge3J9CgojIFNldCB1cCBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIEhpc3RvZ3JhbSBmb3IgYWdlCmhpc3QoZmhzJGFnZSwKICAgICBtYWluID0gIkFnZSIsCiAgICAgeGxhYiA9ICJBZ2UiLAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLAogICAgIGJvcmRlciA9ICJ3aGl0ZSIpCgojIEhpc3RvZ3JhbSBmb3IgY2lnc1BlckRheQpoaXN0KGZocyRjaWdzUGVyRGF5LAogICAgIG1haW4gPSAiQ2lnYXJldHRlcyBQZXIgRGF5IiwKICAgICB4bGFiID0gIkNpZ2FyZXR0ZXMgUGVyIERheSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciB0b3RDaG9sCmhpc3QoZmhzJHRvdENob2wsCiAgICAgbWFpbiA9ICJUb3RhbCBDaG9sZXN0ZXJvbCIsCiAgICAgeGxhYiA9ICJUb3RhbCBDaG9sZXN0ZXJvbCIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBzeXNCUApoaXN0KGZocyRzeXNCUCwKICAgICBtYWluID0gIlN5c3RvbGljIEJsb29kIFByZXNzdXJlIiwKICAgICB4bGFiID0gIlN5c3RvbGljIEJQIiwKICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICBib3JkZXIgPSAid2hpdGUiKQoKIyBSZXNldCBwbG90dGluZyBsYXlvdXQgdG8gZGVmYXVsdApwYXIobWZyb3cgPSBjKDEsIDEpKQoKCgpgYGAKCkZpZ3VyZSBBLiBIaXN0b2dyYW1zIG9mIGFnZSwgQ2lnc1BlckRheSwgdG90Q2hvbCAocmVzcG9uc2UpLCBhbmQgc3lzQlAuIAoKCgoKYGBge3J9CiMgU2V0IHVwIDJ4MiBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIEhpc3RvZ3JhbSBmb3IgZGlhc3RvbGljIGJsb29kIHByZXNzdXJlCmhpc3QoZmhzJGRpYUJQLAogICAgIG1haW4gPSAiRGlhc3RvbGljIEJsb29kIFByZXNzdXJlIiwKICAgICB4bGFiID0gIkRpYXN0b2xpYyBCUCIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBCTUkKaGlzdChmaHMkQk1JLAogICAgIG1haW4gPSAiQm9keSBNYXNzIEluZGV4IChCTUkpIiwKICAgICB4bGFiID0gIkJNSSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBoZWFydCByYXRlCmhpc3QoZmhzJGhlYXJ0UmF0ZSwKICAgICBtYWluID0gIkhlYXJ0IFJhdGUiLAogICAgIHhsYWIgPSAiQmVhdHMgUGVyIE1pbnV0ZSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBnbHVjb3NlCmhpc3QoZmhzJGdsdWNvc2UsCiAgICAgbWFpbiA9ICJHbHVjb3NlIExldmVsIiwKICAgICB4bGFiID0gIkdsdWNvc2UiLAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLAogICAgIGJvcmRlciA9ICJ3aGl0ZSIpCgojIFJlc2V0IHRvIGRlZmF1bHQgcGxvdHRpbmcgbGF5b3V0CnBhcihtZnJvdyA9IGMoMSwgMSkpCgpgYGAKCkZpZ3VyZSBCLiBIaXN0b2dyYW1zIGZvciBkaWFCUCwgQk1JLCBoZWFydFJhdGUsIGFuZCBnbHVjb3NlLiAKCgoKYGBge3J9CgojIFNldCB1cCAyeDIgcGxvdHRpbmcgbGF5b3V0CnBhcihtZnJvdyA9IGMoMiwgMikpCgoKIyBCYXIgcGxvdCBmb3Igc2V4CmJhcnBsb3QodGFibGUoZmhzJHNleCksCiAgICAgICAgbWFpbiA9ICJTZXggRGlzdHJpYnV0aW9uIiwKICAgICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJGZW1hbGUiLCAiTWFsZSIpLAogICAgICAgIHlsYWIgPSAiQ291bnQiKQoKCgojIEJhciBwbG90IGZvciBjdXJyZW50IHNtb2tlcgpiYXJwbG90KHRhYmxlKGZocyRjdXJyZW50U21va2VyKSwKICAgICAgICBtYWluID0gIkN1cnJlbnQgU21va2VyIFN0YXR1cyIsCiAgICAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiTm8iLCAiWWVzIiksCiAgICAgICAgeWxhYiA9ICJDb3VudCIpCgojIEJhciBwbG90IGZvciBCUCBtZWRzCmJhcnBsb3QodGFibGUoZmhzJEJQTWVkcyksCiAgICAgICAgbWFpbiA9ICJCbG9vZCBQcmVzc3VyZSBNZWRpY2F0aW9uIiwKICAgICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJObyIsICJZZXMiKSwKICAgICAgICB5bGFiID0gIkNvdW50IikKCgoKYGBgCgpGaWd1cmUgQy4gQmFyIGNoYXJ0cyBmb3Igc2V4LCBjdXJyZW50U21va2VyIGFuZCBCUE1lZHMuIAoKCmBgYHtyfQoKIyBTZXQgMngyIGxheW91dCBmb3IgYmFyIGNoYXJ0cwpwYXIobWZyb3cgPSBjKDIsIDIpKQoKIyBCYXIgY2hhcnQgZm9yIHByZXZhbGVudFN0cm9rZQpiYXJwbG90KHRhYmxlKGZocyRwcmV2YWxlbnRTdHJva2UpLAogICAgICAgIG1haW4gPSAiUHJldmFsZW50IFN0cm9rZSIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKIyBCYXIgY2hhcnQgZm9yIHByZXZhbGVudEh5cApiYXJwbG90KHRhYmxlKGZocyRwcmV2YWxlbnRIeXApLAogICAgICAgIG1haW4gPSAiUHJldmFsZW50IEh5cGVydGVuc2lvbiIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKIyBCYXIgY2hhcnQgZm9yIGRpYWJldGVzCmJhcnBsb3QodGFibGUoZmhzJGRpYWJldGVzKSwKICAgICAgICBtYWluID0gIkRpYWJldGVzIiwKICAgICAgICB4bGFiID0gIjAgPSBObywgMSA9IFllcyIsCiAgICAgICAgY29sID0gInN0ZWVsYmx1ZSIpCgojIEJhciBjaGFydCBmb3IgVGVuWWVhckNIRApiYXJwbG90KHRhYmxlKGZocyRUZW5ZZWFyQ0hEKSwKICAgICAgICBtYWluID0gIlRlbi1ZZWFyIENIRCIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKYGBgCgpGaWd1cmUgRC4gQmFyIGNoYXJ0cyBmb3IgcHJldmFsZW50U3Ryb2tlLCBwcmV2YWxlbnRIeXAsIGRpYWJldGVzLCBhbmQgVGVuWWVhckNIRCAocmVzcG9uc2UpLiAKCgojIE1JU1NJTkcgVkFMVUVTCgoKSW1wdXRhdGlvbiBpcyBhIGNyaXRpY2FsIHN0ZXAgaW4gZGF0YSBwcmVwcm9jZXNzaW5nLCBlc3BlY2lhbGx5IHdoZW4gd29ya2luZyB3aXRoIGRhdGFzZXRzIGNvbnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMuIEl0IGludm9sdmVzIGVzdGltYXRpbmcgYW5kIHJlcGxhY2luZyBtaXNzaW5nIGRhdGEgd2l0aCBwbGF1c2libGUgdmFsdWVzIGJhc2VkIG9uIHRoZSBhdmFpbGFibGUgaW5mb3JtYXRpb24uCgpGb3IgdGhlIG1pc3NpbmcgY2lnc1BlckRheSB2YWx1ZXMsIGFueSBpbnN0YW5jZXMgdGhhdCBhcmUgbm90IGN1cnJlbnQgc21va2VycyAoY3VycmVudFNtb2tlciA9IDApIGFuZCBoYXZlIG1pc3NpbmcgY2lnc1BlckRheSB3aWxsIGJlIGltcHV0ZWQgd2l0aCB6ZXJvLiBBbGwgb3RoZXIgaW5zdGFuY2VzIHdpdGggbWlzc2luZyBjaWdzUGVyRGF5IHdpbGwgYmUgaW1wdXRlZCB3aXRoIHRoZSBtZWFuLgoKCmBgYHtyfQoKCiMgU3RlcCAxOiBTYXZlIHRoZSBvcmlnaW5hbCBjaWdzUGVyRGF5IGJlZm9yZSBpbXB1dGF0aW9uCmNpZ3NfYmVmb3JlIDwtIGZocyRjaWdzUGVyRGF5CgojIFN0ZXAgMjogSW1wdXRlIHVzaW5nIGN1cnJlbnRTbW9rZXIKbWVhbl9jaWdzX3Ntb2tlcnMgPC0gbWVhbihmaHMkY2lnc1BlckRheVtmaHMkY3VycmVudFNtb2tlciA9PSAxICYgIWlzLm5hKGZocyRjaWdzUGVyRGF5KV0sIG5hLnJtID0gVFJVRSkKZmhzJGNpZ3NQZXJEYXlbaXMubmEoZmhzJGNpZ3NQZXJEYXkpICYgZmhzJGN1cnJlbnRTbW9rZXIgPT0gMF0gPC0gMApmaHMkY2lnc1BlckRheVtpcy5uYShmaHMkY2lnc1BlckRheSkgJiBmaHMkY3VycmVudFNtb2tlciA9PSAxXSA8LSBtZWFuX2NpZ3Nfc21va2VycwoKIyBTdGVwIDM6IENyZWF0ZSBhIGNvbWJpbmVkIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCmNpZ3NfZGYgPC0gcmJpbmQoCiAgZGF0YS5mcmFtZSh2YWx1ZSA9IGNpZ3NfYmVmb3JlLCBncm91cCA9ICJCZWZvcmUiKSwKICBkYXRhLmZyYW1lKHZhbHVlID0gZmhzJGNpZ3NQZXJEYXksIGdyb3VwID0gIkFmdGVyIikKKQoKIyBTdGVwIDQ6IFBsb3QgdGhlIG92ZXJsYXkgZGVuc2l0eSBjdXJ2ZQpnZ3Bsb3QoY2lnc19kZiwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IGdyb3VwLCBjb2xvciA9IGdyb3VwKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKwogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIGNpZ3NQZXJEYXkgQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiQ2lnYXJldHRlcyBQZXIgRGF5IiwgeSA9ICJEZW5zaXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkJlZm9yZSIgPSAibGlnaHRibHVlIiwgIkFmdGVyIiA9ICJzYWxtb24iKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJCZWZvcmUiID0gImxpZ2h0Ymx1ZSIsICJBZnRlciIgPSAic2FsbW9uIikpICsKICB0aGVtZV9taW5pbWFsKCkKCgoKYGBgCgpGaWd1cmUgRS4gUG9zdCBpbXB1dGF0aW9uLCBjaWdzUGVyRGF5IGhhcyB6ZXJvIG1pc3NpbmcgdmFsdWVzLiBUaGUgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGRpc3RyaWJ1dGlvbnMgYXJlIHZlcnkgc2ltaWxhci4KClRoZSBkaXN0cmlidXRpb25zIG9mIENpZ3NQZXJEYXkgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGFyZSB2ZXJ5IHNpbWlsYXIsIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGVmZmVjdGl2ZWx5IHByZXNlcnZlZCB0aGUgb3JpZ2luYWwgZGF0YSBjaGFyYWN0ZXJpc3RpY3MuCgoKTXVsdGlwbGUgaW1wdXRhdGlvbiBieSBjaGFpbmVkIGVxdWF0aW9ucyAoTUlDRSkgaXMgdXNlZCB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMgZm9yIGNpZ1BlckRheSwgQlBNZWRzLCB0b3RDaG9sLCBCTUksIGhlYXJ0UmF0ZSwgYW5kIGdsdWNvc2UuIE1JQ0UgaXMgYW4gaXRlcmF0aXZlIG1ldGhvZCB0aGF0IGNyZWF0ZXMgbXVsdGlwbGUgaW1wdXRlZCBkYXRhc2V0cywgaW1wcm92aW5nIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGJ5IGluY29ycG9yYXRpbmcgdW5jZXJ0YWludHkgaW4gdGhlIG1pc3NpbmcgdmFsdWVzLiBUaGUgaW1wdXRlZCB2YWx1ZXMgYXJlIGV4cGVjdGVkIHRvIHByb3ZpZGUgYSBtb3JlIGNvbXBsZXRlIGRhdGFzZXQgZm9yIHN1YnNlcXVlbnQgYW5hbHlzaXMsIHN1Y2ggYXMgbW9kZWxpbmcgYW5kIHByZWRpY3Rpb24uCgpgYGB7cn0KCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgdGhlIGZlYXR1cmVzIGJlZm9yZSBpbXB1dGF0aW9uIChvcmlnaW5hbCBkYXRhKQpmaHNfbWlzc2luZ19iZWZvcmUgPC0gZmhzWywgYygiY2lnc1BlckRheSIsICJ0b3RDaG9sIiwgIkJNSSIsICJoZWFydFJhdGUiLCAiZ2x1Y29zZSIpXQoKIyBQZXJmb3JtIE1JQ0UgaW1wdXRhdGlvbiB3aGlsZSBmdWxseSBzdXBwcmVzc2luZyBjb25zb2xlIG91dHB1dAppbnZpc2libGUoY2FwdHVyZS5vdXRwdXQoewogIG1pY2VfaW1wdXRhdGlvbiA8LSBtaWNlKGZoc19taXNzaW5nX2JlZm9yZSwgbWV0aG9kID0gJ3BtbScsIG0gPSA1LCBtYXhpdCA9IDUwLCBzZWVkID0gNTAwKQp9KSkKCiMgQ29tcGxldGUgdGhlIGltcHV0ZWQgZGF0YQpmaHNfaW1wdXRlZCA8LSBjb21wbGV0ZShtaWNlX2ltcHV0YXRpb24sIGFjdGlvbiA9IDEpCgojIFByZXBhcmUgZGF0YSBmb3IgcGxvdHRpbmcgKGJlZm9yZSBhbmQgYWZ0ZXIpCmZoc19iZWZvcmUgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcihhcy5tYXRyaXgoZmhzX21pc3NpbmdfYmVmb3JlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9IHJlcChjb2xuYW1lcyhmaHNfbWlzc2luZ19iZWZvcmUpLCBlYWNoID0gbnJvdyhmaHNfbWlzc2luZ19iZWZvcmUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSByZXAoIkJlZm9yZSIsIG5yb3coZmhzX21pc3NpbmdfYmVmb3JlKSAqIG5jb2woZmhzX21pc3NpbmdfYmVmb3JlKSkpCgpmaHNfYWZ0ZXIgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcihhcy5tYXRyaXgoZmhzX2ltcHV0ZWQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSByZXAoY29sbmFtZXMoZmhzX2ltcHV0ZWQpLCBlYWNoID0gbnJvdyhmaHNfaW1wdXRlZCkpLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0ID0gcmVwKCJBZnRlciIsIG5yb3coZmhzX2ltcHV0ZWQpICogbmNvbChmaHNfaW1wdXRlZCkpKQoKIyBDb21iaW5lIHRoZSBkYXRhIGZvciBiZWZvcmUgYW5kIGFmdGVyIGludG8gb25lIGRhdGEgZnJhbWUKZmhzX2NvbWJpbmVkIDwtIHJiaW5kKGZoc19iZWZvcmUsIGZoc19hZnRlcikKCiMgQ3JlYXRlIHRoZSBkZW5zaXR5IHBsb3Qgd2l0aCBnZ3Bsb3QKZ2dwbG90KGZoc19jb21iaW5lZCwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IGRhdGFzZXQsIGNvbG9yID0gZGF0YXNldCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IFBsb3RzOiBCZWZvcmUgYW5kIEFmdGVyIEltcHV0YXRpb24iLCAKICAgICAgIHggPSAiVmFsdWUiLCB5ID0gIkRlbnNpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJCZWZvcmUiID0gImxpZ2h0Ymx1ZSIsICJBZnRlciIgPSAicGluayIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkJlZm9yZSIgPSAibGlnaHRibHVlIiwgIkFmdGVyIiA9ICJwaW5rIikpCgoKYGBgCkZpZ3VyZSBGLiBCZWZvcmUgYW5kIGFmdGVyIGltcHV0YXRpb24gZGVuc2l0eSBjdXJ2ZXMuIAoKClRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSB2YXJpYWJsZXMgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGFyZSB2ZXJ5IHNpbWlsYXIsIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGVmZmVjdGl2ZWx5IHByZXNlcnZlZCB0aGUgb3JpZ2luYWwgZGF0YSBjaGFyYWN0ZXJpc3RpY3MuIFRoZSB1c2Ugb2YgbXVsdGlwbGUgaW1wdXRhdGlvbiBieSBjaGFpbmVkIGVxdWF0aW9ucyAoTUlDRSkgaGFzIGVuc3VyZWQgdGhhdCB0aGUgaW1wdXRlZCB2YWx1ZXMgY2xvc2VseSBmb2xsb3cgdGhlIHBhdHRlcm5zIGFuZCBzcHJlYWQgb2YgdGhlIG9ic2VydmVkIGRhdGEsIG1haW50YWluaW5nIHRoZSBpbnRlZ3JpdHkgb2YgdGhlIHZhcmlhYmxlcyBmb3IgZnVydGhlciBhbmFseXNpcy4gSW4gdHVybiwgdGhlIGltcHV0ZWQgdmFsdWVzIGlzIGFkZGVkIHRvIHRoZSBmaW5hbCB3b3JraW5nIGRhdGFzZXQgZm9yIGFuYWx5c2lzLiAKCgoKYGBge3J9CiMgT3ZlcndyaXRlIG9yaWdpbmFsIGNvbHVtbnMgaW4gZmhzIHdpdGggaW1wdXRlZCB2YWx1ZXMKZmhzJGNpZ3NQZXJEYXkgPC0gZmhzX2ltcHV0ZWQkY2lnc1BlckRheQpmaHMkdG90Q2hvbCA8LSBmaHNfaW1wdXRlZCR0b3RDaG9sCmZocyRCTUkgPC0gZmhzX2ltcHV0ZWQkQk1JCmZocyRoZWFydFJhdGUgPC0gZmhzX2ltcHV0ZWQkaGVhcnRSYXRlCmZocyRnbHVjb3NlIDwtIGZoc19pbXB1dGVkJGdsdWNvc2UKCgoKYGBgCgoKClByaW9yIHRvIG1vZGVsaW5nLCB0aGUgdmFyaWFibGUgQlBNZWRzICh3aGV0aGVyIHRoZSBwYXJ0aWNpcGFudCB3YXMgb24gYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbikgY29udGFpbnMgbWlzc2luZyB2YWx1ZXMuIFRvIGFkZHJlc3MgdGhpcywgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGlzIGZpdHRlZCB1c2luZyByZWxldmFudCBwcmVkaWN0b3JzIChhZ2UsIHN5c0JQLCBkaWFCUCwgY2lnc1BlckRheSwgYW5kIHByZXZhbGVudEh5cCkgYmFzZWQgb24gY2FzZXMgd2l0aCBvYnNlcnZlZCBCUE1lZHMgdmFsdWVzLiBQcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmcm9tIHRoaXMgbW9kZWwgd2VyZSB0aGVuIHVzZWQgdG8gaW1wdXRlIG1pc3NpbmcgdmFsdWVzLCB3aXRoIGEgdGhyZXNob2xkIG9mIDAuNSB0byBjbGFzc2lmeSBpbmRpdmlkdWFscyBhcyBlaXRoZXIgdGFraW5nIG9yIG5vdCB0YWtpbmcgYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbi4KCmBgYHtyfQoKIyBTYXZlIG9yaWdpbmFsIEJQTWVkcyB3aXRoIE5BcyBiZWZvcmUgaW1wdXRhdGlvbgpmaHMkQlBNZWRzX29yaWdpbmFsIDwtIGZocyRCUE1lZHMKCiMgS2VlcCByb3dzIHdoZXJlIEJQTWVkcyBpcyBub3QgbWlzc2luZwpicG1fY29tcGxldGUgPC0gZmhzWyFpcy5uYShmaHMkQlBNZWRzX29yaWdpbmFsKSwgXQoKIyBGaXQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApicG1fbW9kZWwgPC0gZ2xtKEJQTWVkc19vcmlnaW5hbCB+IGFnZSArIHN5c0JQICsgZGlhQlAgKyBjaWdzUGVyRGF5ICsgcHJldmFsZW50SHlwLAogICAgICAgICAgICAgICAgIGRhdGEgPSBicG1fY29tcGxldGUsIGZhbWlseSA9IGJpbm9taWFsKQoKIyBJZGVudGlmeSByb3dzIHdpdGggbWlzc2luZyBCUE1lZHMKYnBtX21pc3NpbmcgPC0gZmhzW2lzLm5hKGZocyRCUE1lZHNfb3JpZ2luYWwpLCBdCgojIElkZW50aWZ5IHJvd3MgYW1vbmcgYnBtX21pc3NpbmcgdGhhdCBoYXZlIGNvbXBsZXRlIHByZWRpY3RvcnMKcHJlZGljdGFibGVfcm93cyA8LSBjb21wbGV0ZS5jYXNlcyhicG1fbWlzc2luZ1ssIGMoImFnZSIsICJzeXNCUCIsICJkaWFCUCIsICJjaWdzUGVyRGF5IiwgInByZXZhbGVudEh5cCIpXSkKCiMgUHJlZGljdCBvbmx5IG9uIHJvd3Mgd2UgY2FuIHByZWRpY3QKaWYgKGFueShwcmVkaWN0YWJsZV9yb3dzKSkgewogIHByZWRpY3RlZF9wcm9icyA8LSBwcmVkaWN0KGJwbV9tb2RlbCwgbmV3ZGF0YSA9IGJwbV9taXNzaW5nW3ByZWRpY3RhYmxlX3Jvd3MsIF0sIHR5cGUgPSAicmVzcG9uc2UiKQogIAogICMgR2V0IHRoZSBvcmlnaW5hbCByb3cgbnVtYmVycyBmb3IgdGhlIG1pc3NpbmcgZGF0YQogIG1pc3Npbmdfcm93X2luZGljZXMgPC0gd2hpY2goaXMubmEoZmhzJEJQTWVkc19vcmlnaW5hbCkpCiAgCiAgIyBGaW5kIGluZGljZXMgdGhhdCBhcmUgcHJlZGljdGFibGUKICBwcmVkaWN0YWJsZV9pbmRpY2VzIDwtIG1pc3Npbmdfcm93X2luZGljZXNbcHJlZGljdGFibGVfcm93c10KICAKICAjIEltcHV0ZSBwcmVkaWN0ZWQgdmFsdWVzIGludG8gdGhlIGNvcnJlY3Qgcm93cwogIGZocyRCUE1lZHNbcHJlZGljdGFibGVfaW5kaWNlc10gPC0gaWZlbHNlKHByZWRpY3RlZF9wcm9icyA+PSAwLjUsIDEsIDApCn0KCgojIE1ha2Ugc3VyZSBmaHMkQlBNZWRzIGFuZCBmaHMkQlBNZWRzX29yaWdpbmFsIGFyZSB0aGVyZQoKIyBDcmVhdGUgYSBsb25nLWZvcm1hdCBkYXRhIGZyYW1lCmJwbV9wbG90X2RhdGEgPC0gZGF0YS5mcmFtZSgKICB2YWx1ZSA9IGMoZmhzJEJQTWVkc19vcmlnaW5hbCwgZmhzJEJQTWVkcyksCiAgc3RhdHVzID0gcmVwKGMoIkJlZm9yZSBJbXB1dGF0aW9uIiwgIkFmdGVyIEltcHV0YXRpb24iKSwgZWFjaCA9IG5yb3coZmhzKSkKKQoKIyBDbGVhbiBiYXIgcGxvdApnZ3Bsb3QoYnBtX3Bsb3RfZGF0YSwgYWVzKHggPSBmYWN0b3IodmFsdWUpLCBmaWxsID0gc3RhdHVzKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnModGl0bGUgPSAiQlBNZWRzIEJlZm9yZSBhbmQgQWZ0ZXIgSW1wdXRhdGlvbiIsCiAgICAgICB4ID0gIkJQTWVkcyAoMCA9IE5vLCAxID0gWWVzKSIsCiAgICAgICB5ID0gIkNvdW50IiwKICAgICAgIGZpbGwgPSAiU3RhdHVzIikgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiMCIgPSAiTm8iLCAiMSIgPSAiWWVzIikpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KQoKYGBgCgpGaWd1cmUgRy4gU2lkZSBieSBzaWRlIGJhciBjaGFydCBvZiBiZWZvcmUgYW5kIGFmdGVyIGxvZ2lzdGljIGltcHV0YXRpb24uCgpUaGUgZGlzdHJpYnV0aW9uIG9mIEJQTWVkcyBiZWZvcmUgYW5kIGFmdGVyIGxvZ2lzdGljIGltcHV0YXRpb24gaXMgdmVyeSBzaW1pbGFyLCBpbmRpY2F0aW5nIHRoYXQgdGhlIGltcHV0YXRpb24gcHJvY2VzcyBwcmVzZXJ2ZWQgdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHdpdGhvdXQgaW50cm9kdWNpbmcgc3Vic3RhbnRpYWwgYmlhcy4KCmBgYHtyfQoKIyBSZW1vdmUgQlBNZWRzX29yaWdpbmFsIGNvbHVtbiBhZnRlciBpbXB1dGF0aW9uIGFuZCBwbG90dGluZwpmaHMkQlBNZWRzX29yaWdpbmFsIDwtIE5VTEwKCmBgYAoKCiMgRkVBVFVSRSBTRUxFQ1RJT04KCgojIyBGZWF0dXJlIFNlbGVjdGlvbgoKVG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluY2x1ZGVkIGluIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcCBwbG90cywgZmVhdHVyZSBzZWxlY3Rpb24gd2FzIHBlcmZvcm1lZCB1c2luZyBhIFJhbmRvbSBGb3Jlc3QgcmVncmVzc2lvbiBtb2RlbC4gVGhlIGdvYWwgaXMgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wb3J0YW50IG51bWVyaWNhbCBwcmVkaWN0b3JzIG9mIFRlblllYXJDSEQsIHNvIHRoYXQgb25seSB0aGUgbW9zdCByZWxldmFudCB2YXJpYWJsZXMgYXJlIGV4YW1pbmVkIGluIHNjYXR0ZXJwbG90cyBhbmQgb3RoZXIgdmlzdWFsaXphdGlvbnMuCgoKYGBge3J9CgoKIyBTdWJzZXQgbnVtZXJpYyBwcmVkaWN0b3JzCm51bWVyaWNfdmFycyA8LSBzYXBwbHkoZmhzLCBpcy5udW1lcmljKQpmaHNfbnVtZXJpYyA8LSBmaHNbLCBudW1lcmljX3ZhcnNdCgojIE1ha2Ugc3VyZSBUZW5ZZWFyQ0hEIGlzIGluY2x1ZGVkCmZoc19udW1lcmljJFRlblllYXJDSEQgPC0gZmhzJFRlblllYXJDSEQgIAoKIyBSYW5kb20gRm9yZXN0IGZvciBudW1lcmljIGZlYXR1cmVzCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpyZl9udW1lcmljIDwtIHJhbmRvbUZvcmVzdChUZW5ZZWFyQ0hEIH4gLiwgZGF0YSA9IGZoc19udW1lcmljLCBpbXBvcnRhbmNlID0gVFJVRSkKCiMgRXh0cmFjdCBpbXBvcnRhbmNlCmltcG9ydGFuY2VfbnVtZXJpYyA8LSBpbXBvcnRhbmNlKHJmX251bWVyaWMpCgojIENyZWF0ZSBpbXBvcnRhbmNlIGRhdGFmcmFtZSBiYXNlZCBvbiBJbmNOb2RlUHVyaXR5CmltcG9ydGFuY2VfbnVtZXJpY19kZiA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gcm93bmFtZXMoaW1wb3J0YW5jZV9udW1lcmljKSwKICBJbXBvcnRhbmNlID0gaW1wb3J0YW5jZV9udW1lcmljWywgIkluY05vZGVQdXJpdHkiXQopCgojIFNvcnQgYnkgaW1wb3J0YW5jZQppbXBvcnRhbmNlX251bWVyaWNfZGYgPC0gaW1wb3J0YW5jZV9udW1lcmljX2RmW29yZGVyKC1pbXBvcnRhbmNlX251bWVyaWNfZGYkSW1wb3J0YW5jZSksIF0KCiMgVmlldyByZXN1bHRzCnByaW50KGltcG9ydGFuY2VfbnVtZXJpY19kZikKCmBgYAoKVGFibGUgMi4gTW9zdCBpbXBvcnRhbnQgbnVtZXJpYyBmZWF0dXJlcy4gCgoKVXNpbmcgYSBSYW5kb20gRm9yZXN0IHJlZ3Jlc3Npb24gbW9kZWwsIGZlYXR1cmUgaW1wb3J0YW5jZSB3YXMgZXZhbHVhdGVkIGZvciB0aGUgbnVtZXJpY2FsIHByZWRpY3RvcnMgb2YgVGVuWWVhckNIRC4gVGhlIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAoc3lzQlApLCBib2R5IG1hc3MgaW5kZXggKEJNSSksIGFuZCB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgYXJlIHRoZSBtb3N0IGluZmx1ZW50aWFsIHZhcmlhYmxlcyBpbiBwcmVkaWN0aW5nIHRlbi15ZWFyIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2Ugcmlzay4gT3RoZXIgbm90YWJsZSBwcmVkaWN0b3JzIGluY2x1ZGUgZ2x1Y29zZSwgYWdlLCBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKGRpYUJQKSwgYW5kIGhlYXJ0IHJhdGUsIHdoaWxlIGNpZ2FyZXR0ZXMgcGVyIGRheSAoY2lnc1BlckRheSkgaGFkIHRoZSBsb3dlc3QgcmVsYXRpdmUgaW1wb3J0YW5jZSBhbW9uZyB0aGUgbnVtZXJpY2FsIGZlYXR1cmVzIGNvbnNpZGVyZWQuCgoKCgpUbyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgY2F0ZWdvcmljYWwgcHJlZGljdG9ycyBvZiBUZW5ZZWFyQ0hELCBhIFJhbmRvbSBGb3Jlc3QgY2xhc3NpZmljYXRpb24gbW9kZWwgd2FzIGZpdCB1c2luZyBvbmx5IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuIFRoaXMgZmVhdHVyZSBzZWxlY3Rpb24gc3RlcCBoZWxwcyBmb2N1cyBwYWlyd2lzZSBhbmFseXNlcyBvbiB0aGUgbW9zdCByZWxldmFudCBjYXRlZ29yaWNhbCBmYWN0b3JzLCBpbXByb3ZpbmcgdGhlIGNsYXJpdHkgYW5kIGludGVycHJldGFiaWxpdHkgb2YgdGhlIHZpc3VhbGl6YXRpb25zLgoKCmBgYHtyfQoKIyBTdWJzZXQgb25seSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKZmFjdG9yX3ZhcnMgPC0gc2FwcGx5KGZocywgaXMuZmFjdG9yKQpmaHNfZmFjdG9yIDwtIGZoc1ssIGZhY3Rvcl92YXJzXQoKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBmaHNfZmFjdG9yIGRhdGFzZXQKI2NvbFN1bXMoaXMubmEoZmhzX2ZhY3RvcikpCgojIFJlbW92ZSBCUE1lZHNfb3JpZ2luYWwgaWYgaXQgZXhpc3RzCmZoc19mYWN0b3IkQlBNZWRzX29yaWdpbmFsIDwtIE5VTEwKCiMgRW5zdXJlIHRoZSB0YXJnZXQgdmFyaWFibGUgVGVuWWVhckNIRCBpcyBhIGZhY3RvciBpbiB0aGUgb3JpZ2luYWwgZGF0YQpmaHMkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IoZmhzJFRlblllYXJDSEQpCgojIEFkZCBUZW5ZZWFyQ0hEIGJhY2sgdG8gdGhlIGZhY3RvciBkYXRhc2V0IGlmIGl0IHdhcyBkcm9wcGVkCmZoc19mYWN0b3IkVGVuWWVhckNIRCA8LSBmaHMkVGVuWWVhckNIRAoKIyBUcmFpbiBSYW5kb20gRm9yZXN0IGNsYXNzaWZpZXIgb24gY2F0ZWdvcmljYWwgZmVhdHVyZXMKcmZfZmFjdG9yIDwtIHJhbmRvbUZvcmVzdChUZW5ZZWFyQ0hEIH4gLiwgZGF0YSA9IGZoc19mYWN0b3IsIGltcG9ydGFuY2UgPSBUUlVFKQoKIyBHZXQgaW1wb3J0YW5jZSB2YWx1ZXMKaW1wb3J0YW5jZV9mYWN0b3IgPC0gaW1wb3J0YW5jZShyZl9mYWN0b3IpCgojIENvbnZlcnQgdG8gYSBjbGVhbiBkYXRhIGZyYW1lCmltcG9ydGFuY2VfZmFjdG9yX2RmIDwtIGRhdGEuZnJhbWUoCiAgVmFyaWFibGUgPSByb3duYW1lcyhpbXBvcnRhbmNlX2ZhY3RvciksCiAgSW1wb3J0YW5jZSA9IGltcG9ydGFuY2VfZmFjdG9yWywgIk1lYW5EZWNyZWFzZUdpbmkiXQopCgojIFNvcnQgYnkgaW1wb3J0YW5jZQppbXBvcnRhbmNlX2ZhY3Rvcl9kZiA8LSBpbXBvcnRhbmNlX2ZhY3Rvcl9kZltvcmRlcigtaW1wb3J0YW5jZV9mYWN0b3JfZGYkSW1wb3J0YW5jZSksIF0KCiMgUHJpbnQKcHJpbnQoaW1wb3J0YW5jZV9mYWN0b3JfZGYpCgpgYGAKClRhYmxlIDMuIE1vc3QgaW1wb3J0YW50IGNhdGVnb3JpY2FsIGZlYXR1cmVzLgoKClVzaW5nIGEgUmFuZG9tIEZvcmVzdCBjbGFzc2lmaWNhdGlvbiBtb2RlbCwgZmVhdHVyZSBpbXBvcnRhbmNlIHdhcyBldmFsdWF0ZWQgZm9yIHRoZSBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIG9mIFRlblllYXJDSEQuIFRoZSByZXN1bHRzIHNob3cgdGhhdCBwcmV2YWxlbnQgaHlwZXJ0ZW5zaW9uIChwcmV2YWxlbnRIeXApIGlzIGJ5IGZhciB0aGUgbW9zdCBpbmZsdWVudGlhbCBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgZm9sbG93ZWQgYnkgZGlhYmV0ZXMgYW5kIHNleC4gT3RoZXIgY2F0ZWdvcmljYWwgZmVhdHVyZXMgc3VjaCBhcyBwcmV2YWxlbnQgc3Ryb2tlLCB1c2Ugb2YgYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbiAoQlBNZWRzKSwgYW5kIGN1cnJlbnQgc21va2luZyBzdGF0dXMgaGFkIGxvd2VyIHJlbGF0aXZlIGltcG9ydGFuY2UgaW4gcHJlZGljdGluZyB0ZW4teWVhciBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIHJpc2suCgoKCiMgUEFJUldJU0UgUkVMQVRJT05TSElQUwoKVG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgcHJlZGljdG9ycyBvZiBUZW5ZZWFyQ0hELCBwYWlyd2lzZSBzY2F0dGVycGxvdHMgYXJlIGdlbmVyYXRlZCBmb3IgdGhlIHRvcCBmaXZlIHZhcmlhYmxlcyBpZGVudGlmaWVkIHRocm91Z2ggUmFuZG9tIEZvcmVzdCBmZWF0dXJlIHNlbGVjdGlvbi4gVGhlc2UgaW5jbHVkZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAoc3lzQlApLCBib2R5IG1hc3MgaW5kZXggKEJNSSksIHRvdGFsIGNob2xlc3Rlcm9sICh0b3RDaG9sKSwgZ2x1Y29zZSwgYW5kIGFnZS4gQW5hbHl6aW5nIHRoZXNlIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgaGVscHMgdG8gdmlzdWFsbHkgdW5kZXJzdGFuZCBob3cgdGhlc2UgZmVhdHVyZXMgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlciBhbmQgd2l0aCB0aGUgdGFyZ2V0IHZhcmlhYmxlLCBUZW5ZZWFyQ0hELiAKCgpgYGB7cn0KCiMgU2VsZWN0IHRoZSB0b3AgNSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgZmVhdHVyZXMKdG9wX2ZlYXR1cmVzIDwtIGZoc1ssIGMoInN5c0JQIiwgIkJNSSIsICJ0b3RDaG9sIiwgImdsdWNvc2UiLCAiYWdlIildCgojIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcih0b3BfZmVhdHVyZXMpCgojIFByaW50IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggd2l0aCBudW1iZXJzCiNwcmludChjb3JfbWF0cml4KQoKIyBDcmVhdGUgYSBjbGVhbiBjb3JyZWxhdGlvbiBwbG90IHdpdGggbnVtYmVycyBkaXNwbGF5ZWQgaW5zaWRlIHRoZSBjZWxscwpjb3JycGxvdChjb3JfbWF0cml4LCAKICAgICAgICAgbWV0aG9kID0gImNvbG9yIiwgICAgICAgICAjIENvbG9yLWJhc2VkIHZpc3VhbGl6YXRpb24KICAgICAgICAgdHlwZSA9ICJmdWxsIiwgICAgICAgICAgICAjIEZ1bGwgbWF0cml4IGRpc3BsYXkKICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCAid2hpdGUiLCAiYmx1ZSIpKSgyMDApLCAjIENvbG9yIGdyYWRpZW50CiAgICAgICAgIHRsLmNleCA9IDAuOCwgICAgICAgICAgICAgIyBBZGp1c3QgbGFiZWwgc2l6ZQogICAgICAgICBudW1iZXIuY2V4ID0gMC44LCAgICAgICAgICMgQWRqdXN0IGNvcnJlbGF0aW9uIG51bWJlciBzaXplCiAgICAgICAgIGFkZENvZWYuY29sID0gImJsYWNrIiwgICAgIyBBZGQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGluIGJsYWNrCiAgICAgICAgIHRpdGxlID0gIkNvcnJlbGF0aW9uIE1hdHJpeCBmb3IgVG9wIDUgTnVtZXJpY2FsIEZlYXR1cmVzIiwgCiAgICAgICAgIG1hciA9IGMoMCwwLDEsMCkpICAgICAgICAgIyBNYXJnaW5zIGFyb3VuZCB0aGUgcGxvdApgYGAKCkZpZ3VyZSBILiBDb3JyZWxhdGlvbiBtYXRyaXggZm9yIGNvbnRpbnVvdXMgZmVhdHVyZXMuIAoKCgpUaGUgc2NhdHRlcnBsb3QgbWF0cml4IGJlbG93IGRpc3BsYXlzIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgZml2ZSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgZmVhdHVyZXMgaWRlbnRpZmllZCBmb3IgcHJlZGljdGluZyBjb3JvbmFyeSBoZWFydCBkaXNlYXNlLiBUaGVzZSBmZWF0dXJlcyBpbmNsdWRlIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChzeXNCUCksIGJvZHkgbWFzcyBpbmRleCAoQk1JKSwgdG90YWwgY2hvbGVzdGVyb2wgKHRvdENob2wpLCBnbHVjb3NlLCBhbmQgYWdlLiBUaGUgc2NhdHRlcnBsb3RzIHByb3ZpZGUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgaG93IHRoZXNlIHZhcmlhYmxlcyByZWxhdGUgdG8gb25lIGFub3RoZXIsIHdpdGggZWFjaCBwbG90IHNob3djYXNpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIHBvaW50cyBiZXR3ZWVuIHR3byBmZWF0dXJlcy4gVGhlIGluY2x1c2lvbiBvZiBib290c3RyYXBwaW5nIHJlZHVjZXMgY2x1dHRlciBieSBzYW1wbGluZyAzMCUgb2YgdGhlIGRhdGEsIGFsbG93aW5nIGZvciBjbGVhcmVyIGluc2lnaHRzIGludG8gcG90ZW50aWFsIHBhdHRlcm5zIGFuZCBhc3NvY2lhdGlvbnMuCgpgYGB7cn0KCgojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBTZWxlY3QgdGhlIHRvcCA1IG1vc3QgaW1wb3J0YW50IG51bWVyaWNhbCBmZWF0dXJlcwp0b3BfZmVhdHVyZXMgPC0gZmhzWywgYygic3lzQlAiLCAiQk1JIiwgInRvdENob2wiLCAiZ2x1Y29zZSIsICJhZ2UiKV0KCiMgQm9vdHN0cmFwIHRoZSBkYXRhIChyZWR1Y2Ugc2l6ZSB0byAzMCUgdG8gZGVjbHV0dGVyIHNjYXR0ZXJwbG90cykKc2V0LnNlZWQoMTIzKSAgIyBGb3IgcmVwcm9kdWNpYmlsaXR5CnRvcF9mZWF0dXJlc19zYW1wbGUgPC0gdG9wX2ZlYXR1cmVzW3NhbXBsZSgxOm5yb3codG9wX2ZlYXR1cmVzKSwgMC4zICogbnJvdyh0b3BfZmVhdHVyZXMpKSwgXQoKIyBDcmVhdGUgdGhlIHNjYXR0ZXJwbG90IG1hdHJpeCBtYW51YWxseQpwYWlyc19saXN0IDwtIGxpc3QoKQoKIyBMb29wIG92ZXIgYWxsIHBhaXJzIG9mIGZlYXR1cmVzIHRvIGNyZWF0ZSBpbmRpdmlkdWFsIHNjYXR0ZXJwbG90cwpmb3IoaSBpbiAxOihuY29sKHRvcF9mZWF0dXJlc19zYW1wbGUpLTEpKSB7CiAgZm9yKGogaW4gKGkrMSk6bmNvbCh0b3BfZmVhdHVyZXNfc2FtcGxlKSkgewogICAgcGFpcnNfbGlzdFtbcGFzdGUobmFtZXModG9wX2ZlYXR1cmVzX3NhbXBsZSlbaV0sIG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdLCBzZXAgPSAiXyIpXV0gPC0gCiAgICAgIGdncGxvdCh0b3BfZmVhdHVyZXNfc2FtcGxlLCBhZXNfc3RyaW5nKHggPSBuYW1lcyh0b3BfZmVhdHVyZXNfc2FtcGxlKVtpXSwgeSA9IG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdKSkgKwogICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBzaXplID0gMC41KSArIAogICAgICBsYWJzKHRpdGxlID0gcGFzdGUobmFtZXModG9wX2ZlYXR1cmVzX3NhbXBsZSlbaV0sICJ2cyIsIG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KQogICAgICApCiAgfQp9CgojIEFycmFuZ2UgYWxsIHNjYXR0ZXJwbG90cyBpbiBhIGdyaWQgbGF5b3V0CmdyaWQuYXJyYW5nZShncm9icyA9IHBhaXJzX2xpc3QsIG5jb2wgPSA1KSAgIyBBcnJhbmdlIGluIDUgY29sdW1ucywgYWRqdXN0IG5jb2wgaWYgbmVlZGVkCgpgYGAKCkZpZ3VyZSBJLiBTY2F0dGVycGxvdHMgb2YgdGhlIGZpdmUgbW9zdCBpbXBvcnRhbnQgbnVtZXJpYyBmZWF0dXJlcy4gCgoKVGhlIHBhaXJ3aXNlIHNjYXR0ZXJwbG90cyByZXZlYWwgc2V2ZXJhbCBtb2RlcmF0ZSBsaW5lYXIgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgdG9wIGZlYXR1cmVzLiBTcGVjaWZpY2FsbHksIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChzeXNCUCkgYW5kIGJvZHkgbWFzcyBpbmRleCAoQk1JKSBleGhpYml0IGEgbW9kZXJhdGUgbGluZWFyIHJlbGF0aW9uc2hpcCwgYXMgZG8gc3lzQlAgYW5kIHRvdGFsIGNob2xlc3Rlcm9sICh0b3RDaG9sKSwgQk1JIGFuZCB0b3RDaG9sLCBzeXNCUCBhbmQgYWdlLCBhbmQgdG90Q2hvbCBhbmQgYWdlLiBUaGVzZSByZWxhdGlvbnNoaXBzIHN1Z2dlc3QgdGhhdCBhcyBvbmUgdmFyaWFibGUgaW5jcmVhc2VzLCB0aGVyZSB0ZW5kcyB0byBiZSBhIGNvcnJlc3BvbmRpbmcgaW5jcmVhc2UgaW4gdGhlIG90aGVyLCBpbmRpY2F0aW5nIGEgbW9kZXJhdGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGVzZSBrZXkgaGVhbHRoIGZhY3RvcnMuCgoKCgpgYGB7cn0KIyBTZXQgdXAgdGhlIHBsb3R0aW5nIGFyZWEgdG8gaGF2ZSAyIHJvd3MgYW5kIDMgY29sdW1ucwpwYXIobWZyb3cgPSBjKDIsIDMpKQoKIyBMaXN0IG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBjb21wYXJlIHdpdGggVGVuWWVhckNIRApjYXRlZ29yaWNhbF92YXJzIDwtIGMoInNleCIsICJjdXJyZW50U21va2VyIiwgIkJQTWVkcyIsICJwcmV2YWxlbnRTdHJva2UiLCAicHJldmFsZW50SHlwIiwgImRpYWJldGVzIikKCiMgTG9vcCB0aHJvdWdoIGVhY2ggY2F0ZWdvcmljYWwgdmFyaWFibGUKZm9yICh2YXIgaW4gY2F0ZWdvcmljYWxfdmFycykgewogICMgQ3JlYXRlIG1vc2FpYyBwbG90IGZvciBlYWNoIHZhcmlhYmxlIGNvbXBhcmVkIHdpdGggVGVuWWVhckNIRAogIG1vc2FpY3Bsb3QodGFibGUoZmhzW1t2YXJdXSwgZmhzJFRlblllYXJDSEQpLCBtYWluID0gcGFzdGUoIlRlblllYXJDSEQgdnMiLCB2YXIpLCAKICAgICAgICAgICAgIGNvbG9yID0gYygibGlnaHRibHVlIiwgInBpbmsiKSwgCiAgICAgICAgICAgICB4bGFiID0gcGFzdGUodmFyKSwgeWxhYiA9ICJUZW5ZZWFyQ0hEIiwKICAgICAgICAgICAgIGxhcyA9IDEpCiAgIyBBZGQgbGFiZWxzIGZvciAxID0gWWVzLCAwID0gTm8KICB0ZXh0KHggPSAxLCB5ID0gMSwgbGFiZWxzID0gIjEgPSBZZXMiLCBjb2wgPSAiYmxhY2siLCBwb3MgPSA0KQogIHRleHQoeCA9IDEsIHkgPSAyLCBsYWJlbHMgPSAiMCA9IE5vIiwgY29sID0gImJsYWNrIiwgcG9zID0gNCkKfQoKCgpgYGAKCkZpZ3VyZSBKLiBNb3NhaWMgcGxvdHMgb2YgcmVsYXRpb25zaGlwcyBvZiBjYXRlZ29yaWNhbCBmZWF0dXJlcy4gCgoKVGhlIG1vc2FpYyBwbG90cyByZXZlYWwgdGhhdCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIGJldHdlZW4gVGVuWWVhckNIRCBhbmQgYWxsIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGV4Y2VwdCBmb3Ig4oCcY3VycmVudFNtb2tlci7igJ0gU3BlY2lmaWNhbGx5LCB2YXJpYWJsZXMgbGlrZSDigJxzZXgs4oCdIOKAnEJQTWVkcyzigJ0g4oCccHJldmFsZW50U3Ryb2tlLOKAnSDigJxwcmV2YWxlbnRIeXAs4oCdIGFuZCDigJxkaWFiZXRlc+KAnSBzaG93IGNsZWFyIHBhdHRlcm5zIHdoZXJlIHRoZSBkaXN0cmlidXRpb24gb2YgVGVuWWVhckNIRCBkaWZmZXJzIGFjcm9zcyB0aGVpciBsZXZlbHMuIEhvd2V2ZXIsIOKAnGN1cnJlbnRTbW9rZXLigJ0gZG9lcyBub3QgYXBwZWFyIHRvIGV4aGliaXQgYSBzdHJvbmcgYXNzb2NpYXRpb24gd2l0aCB0aGUgb3V0Y29tZSwgc3VnZ2VzdGluZyB0aGF0IHNtb2tpbmcgc3RhdHVzIG1heSBub3QgYmUgYSBzdHJvbmcgcHJlZGljdG9yIG9mIFRlblllYXJDSEQgaW4gdGhpcyBkYXRhc2V0LgoKCgojIEZFQVRVUkUgRU5HSU5FRVIgCgpUbyBlbmhhbmNlIG1vZGVsIHBlcmZvcm1hbmNlIGFuZCB1bmNvdmVyIG1lYW5pbmdmdWwgcGF0dGVybnMsIGZlYXR1cmUgZW5naW5lZXJpbmcgd2FzIGFwcGxpZWQgdG8gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgZGF0YXNldC4gVGhpcyBwcm9jZXNzIGluY2x1ZGVkIGNyZWF0aW5nIG5ldyB2YXJpYWJsZXMsIHN1Y2ggYXMgY2x1c3RlcnMsIGludGVyYWN0aW9uIHRlcm1zLCBhbmQgY2xpbmljYWxseSByZWxldmFudCByaXNrIGluZGljYXRvcnMsIHRvIGJldHRlciBjYXB0dXJlIGNvbXBsZXggcmVsYXRpb25zaGlwcyB3aXRoIHRlbi15ZWFyIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2Ugcmlzay4KCgoKYGBge3J9CgojIFJlbW92ZSBub24tbnVtZXJpYyBjb2x1bW5zIChsaWtlIGZhY3RvcnMgYW5kIHJlc3BvbnNlIHZhcmlhYmxlKQpmaHNfbnVtZXJpYyA8LSBmaHNbc2FwcGx5KGZocywgaXMubnVtZXJpYyldCmZoc19zY2FsZWQgPC0gc2NhbGUoZmhzX251bWVyaWMpICAjIFN0YW5kYXJkaXplIHRoZSBmZWF0dXJlcwoKc2V0LnNlZWQoMTIzKQoKIyBTdGFuZGFyZGl6ZSBudW1lcmljIHZhcmlhYmxlcwpmaHNfc2NhbGVkIDwtIHNjYWxlKGZoc1ssIHNhcHBseShmaHMsIGlzLm51bWVyaWMpXSkKCiMgQ29tcHV0ZSBXU1MgZm9yIGsgPSAxIHRvIDEwCndzcyA8LSBzYXBwbHkoMToxMCwgZnVuY3Rpb24oayl7CiAga21lYW5zKGZoc19zY2FsZWQsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAxMCkkdG90LndpdGhpbnNzCn0pCgojIFBsb3QgRWxib3cgQ3VydmUKcGxvdCgxOjEwLCB3c3MsIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLAogICAgIHhsYWIgPSAiTnVtYmVyIG9mIGNsdXN0ZXJzIEsiLAogICAgIHlsYWIgPSAiVG90YWwgd2l0aGluLWNsdXN0ZXJzIHN1bSBvZiBzcXVhcmVzIiwKICAgICBtYWluID0gIkVsYm93IE1ldGhvZCBmb3IgRGV0ZXJtaW5pbmcgT3B0aW1hbCBLIikKCgojIFJ1biBrLW1lYW5zIHdpdGggY2hvc2VuIG51bWJlciBvZiBjbHVzdGVycyAoZS5nLiwgMykKa21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoZmhzX3NjYWxlZCwgY2VudGVycyA9IDMsIG5zdGFydCA9IDI1KQoKIyBBZGQgY2x1c3RlciB0byB0aGUgZGF0YXNldApmaHMkQ2x1c3RlciA8LSBhcy5mYWN0b3Ioa21lYW5zX3Jlc3VsdCRjbHVzdGVyKQoKIyBSdW4gUENBIGp1c3QgZm9yIHBsb3R0aW5nICgyRCByZXByZXNlbnRhdGlvbikKcGNhX3Jlc3VsdCA8LSBwcmNvbXAoZmhzX3NjYWxlZCkKCiMgQ3JlYXRlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCnBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKFBDMSA9IHBjYV9yZXN1bHQkeFssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgIFBDMiA9IHBjYV9yZXN1bHQkeFssMl0sCiAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPSBmaHMkQ2x1c3RlcikKCiMgUGxvdCB0aGUgY2x1c3RlcnMKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBDbHVzdGVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhID0gMC44KSArCiAgbGFicyh0aXRsZSA9ICJLLU1lYW5zIENsdXN0ZXJpbmcgKFZpc3VhbGl6ZWQgdmlhIFBDQSkiLAogICAgICAgeCA9ICJQcmluY2lwYWwgQ29tcG9uZW50IDEiLAogICAgICAgeSA9ICJQcmluY2lwYWwgQ29tcG9uZW50IDIiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCgoKYGBgCgpGaWd1cmUgSy4gVmlzdWFsaXphdGlvbiBvZiBjbHVzdGVycy4gCgoKVGhlIGNsdXN0ZXIgYW5hbHlzaXMgcmV2ZWFsZWQgdGhyZWUgd2VsbC1zZXBhcmF0ZWQgYW5kIGRpc3RpbmN0IGdyb3VwcyB3aXRoaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgZGF0YXNldCwgc3VnZ2VzdGluZyBtZWFuaW5nZnVsIHVuZGVybHlpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGEuIFRoZXNlIGNsdXN0ZXJzIG1heSByZXByZXNlbnQgZGlmZmVyZW50IGNhcmRpb3Zhc2N1bGFyIHJpc2sgcHJvZmlsZXMsIHdoaWNoIGNvdWxkIGhlbHAgaW5mb3JtIHRhcmdldGVkIHByZXZlbnRpb24gb3IgaW50ZXJ2ZW50aW9uIHN0cmF0ZWdpZXMuCgoKCiMgTUVUSE9ET0xPR1k6IFByZWRpY3RpdmUgTW9kZWxpbmcgQXBwcm9hY2hlcwoKUHJldmlvdXMgbWV0aG9kcywgc3VjaCBhcyByZWdyZXNzaW9uIG1vZGVscyAoaW5jbHVkaW5nIHJlZ3VsYXJpemVkIHZlcnNpb25zIGxpa2UgTGFzc28gYW5kIFJpZGdlKSBhbmQgU1ZNcywgd2VyZSB1c2VkIHRvIGFkZHJlc3MgdGhlIGZvcm11bGF0ZWQgcXVlc3Rpb25zLCBleGNlbGxpbmcgaW4gbGluZWFyIHJlbGF0aW9uc2hpcHMgYW5kIHNtYWxsZXIgZGF0YXNldHMuIEhvd2V2ZXIsIHRoZXNlIG1vZGVscyBjYW4gc3RydWdnbGUgd2l0aCBub24tbGluZWFyIGRhdGEgb3IgaGlnaC1kaW1lbnNpb25hbCBmZWF0dXJlIHNwYWNlcy4KCkNBUlQsIEJhZ2dpbmcsIGFuZCBSYW5kb20gRm9yZXN0cyBvZmZlciBhZHZhbnRhZ2VzIGJ5IGNhcHR1cmluZyBjb21wbGV4LCBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMgd2l0aG91dCB0aGUgbmVlZCBmb3IgbGluZWFyIGFzc3VtcHRpb25zLiBSYW5kb20gRm9yZXN0cywgaW4gcGFydGljdWxhciwgaW1wcm92ZSB1cG9uIEJhZ2dpbmcgYnkgY29uc3RydWN0aW5nIG11bHRpcGxlIGRlY2lzaW9uIHRyZWVzIGFuZCBhdmVyYWdpbmcgdGhlaXIgb3V0cHV0cywgd2hpY2ggaGVscHMgdG8gZnVydGhlciByZWR1Y2UgdmFyaWFuY2UgYW5kIGF2b2lkIG92ZXJmaXR0aW5nLiBXaGlsZSBDQVJUIGlzIGludGVycHJldGFibGUsIGl0IG1heSBvdmVyZml0IHdpdGhvdXQgcHJvcGVyIHBydW5pbmcuIEJhZ2dpbmcgaGVscHMgcmVkdWNlIHZhcmlhbmNlIGJ5IGNvbWJpbmluZyBtdWx0aXBsZSBtb2RlbHMsIGFuZCBSYW5kb20gRm9yZXN0cyBmdXJ0aGVyIGltcHJvdmUgcGVyZm9ybWFuY2Ugd2l0aCBlbnNlbWJsZSBsZWFybmluZy4gRWFjaCBtZXRob2QgaGFzIGl0cyBzdHJlbmd0aHM6IHJlZ3Jlc3Npb24gbW9kZWxzIGFuZCBTVk1zIGV4Y2VsIGF0IGhhbmRsaW5nIHNtYWxsZXIsIHNpbXBsZXIgZGF0YXNldHMgd2l0aCBsaW5lYXIgb3IgcmVndWxhcml6ZWQgcmVsYXRpb25zaGlwcywgd2hpbGUgQ0FSVCwgQmFnZ2luZywgYW5kIFJhbmRvbSBGb3Jlc3RzIHNoaW5lIHdpdGggbGFyZ2VyIGRhdGFzZXRzIGFuZCBtb3JlIGNvbXBsZXggaW50ZXJhY3Rpb25zLiBUaGUgdHJhZGUtb2ZmIGJldHdlZW4gaW50ZXJwcmV0YWJpbGl0eSBhbmQgcGVyZm9ybWFuY2UgaXMgYSBrZXkgY29uc2lkZXJhdGlvbiB3aGVuIHNlbGVjdGluZyB0aGUgYXBwcm9wcmlhdGUgbW9kZWwgZm9yIGEgZ2l2ZW4gdGFzay4KCgojIENBUlQgUkVHUkVTU0lPTgoKQ0FSVCAoQ2xhc3NpZmljYXRpb24gYW5kIFJlZ3Jlc3Npb24gVHJlZXMpIHJlZ3Jlc3Npb24gaXMgYSBkZWNpc2lvbiB0cmVlIGFsZ29yaXRobSB1c2VkIHRvIHByZWRpY3QgY29udGludW91cyBvdXRjb21lcywgc3VjaCBhcyB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgZGF0YXNldC4gVGhlIG1vZGVsIGRldmVsb3BtZW50IGZvbGxvd3MgdGhlc2Uga2V5IGNvbXBvbmVudHM6CgoJMS4JU3BsaXR0aW5nIENyaXRlcmlvbjogVGhlIHRyZWUgcmVjdXJzaXZlbHkgcGFydGl0aW9ucyB0aGUgZGF0YSBieSBjaG9vc2luZyBzcGxpdHMgdGhhdCBtaW5pbWl6ZSB0aGUgc3VtIG9mIHNxdWFyZWQgZXJyb3JzIChTU0UpIHdpdGhpbiB0aGUgcmVzdWx0aW5nIG5vZGVzLCBhaW1pbmcgdG8gY3JlYXRlIGhvbW9nZW5vdXMgZ3JvdXBzIGJhc2VkIG9uIHByZWRpY3RvcnMgbGlrZSBhZ2UsIEJNSSwgYW5kIGJsb29kIHByZXNzdXJlLgoJMi4JVHJlZSBTdHJ1Y3R1cmUgYW5kIFN0b3BwaW5nIFJ1bGVzOiBUbyBwcmV2ZW50IHRoZSB0cmVlIGZyb20gZ3Jvd2luZyB0b28gY29tcGxleCwgc3RvcHBpbmcgcnVsZXMgc3VjaCBhcyBtaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcmVxdWlyZWQgdG8gc3BsaXQgYSBub2RlIChtaW5zcGxpdCksIG1pbmltdW0gc2l6ZSBvZiB0ZXJtaW5hbCBub2RlcyAobWluYnVja2V0KSwgYW5kIG1heGltdW0gdHJlZSBkZXB0aCAobWF4ZGVwdGgpIGFyZSBhcHBsaWVkLgoJMy4JUHJ1bmluZzogQWZ0ZXIgdGhlIGZ1bGwgdHJlZSBpcyBncm93biwgY29zdC1jb21wbGV4aXR5IHBydW5pbmcgaXMgdXNlZCB0byBhdm9pZCBvdmVyZml0dGluZy4gVGhpcyBpbnZvbHZlcyBldmFsdWF0aW5nIHN1YnRyZWVzIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24gYW5kIHNlbGVjdGluZyB0aGUgb3B0aW1hbCBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHRoYXQgYmFsYW5jZXMgbW9kZWwgYWNjdXJhY3kgd2l0aCBzaW1wbGljaXR5LgoJNC4JSW50ZXJwcmV0YWJpbGl0eTogVGhlIGZpbmFsIHRyZWUgc3RydWN0dXJlIHByb3ZpZGVzIGEgdHJhbnNwYXJlbnQgbW9kZWwgc2hvd2luZyBob3cgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBwcmVkaWN0b3JzIGxlYWQgdG8gZGlmZmVyZW50IHByZWRpY3RlZCBjaG9sZXN0ZXJvbCBsZXZlbHMuCgojIyBJbml0aWFsIFJlZ3Jlc3Npb24gVHJlZQoKQW4gaW5pdGlhbCByZWdyZXNzaW9uIHRyZWUgd2FzIGJ1aWx0IHRvIGV4cGxvcmUgaG93IHZhcmlvdXMgaGVhbHRoIGFuZCBsaWZlc3R5bGUgZmFjdG9ycyBwcmVkaWN0IHRvdGFsIGNob2xlc3Rlcm9sIGxldmVscyBpbiB0aGUgRnJhbWluZ2hhbSBkYXRhc2V0LiBCeSBzZXR0aW5nIHJlbGF0aXZlbHkgZmxleGlibGUgdHJlZSBwYXJhbWV0ZXJzLCB0aGUgbW9kZWwgd2FzIGFibGUgdG8gaWRlbnRpZnkga2V5IHZhcmlhYmxlcyBhbmQgcG90ZW50aWFsIG5vbmxpbmVhciByZWxhdGlvbnNoaXBzIHRocm91Z2ggcmVjdXJzaXZlIHBhcnRpdGlvbmluZy4KCmBgYHtyfQoKIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEyMykKCgojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgKDcwJSkgYW5kIHRlc3RpbmcgKDMwJSkgc2V0cwp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmhzJFRlblllYXJDSEQsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkKdHJhaW5fZGF0YSA8LSBmaHNbdHJhaW5JbmRleCwgXQp0ZXN0LmRhdGEgPC0gZmhzWy10cmFpbkluZGV4LCBdCgoKIyBCdWlsZCB0aGUgaW5pdGlhbCBDQVJUIHJlZ3Jlc3Npb24gdHJlZSBmb3IgcHJlZGljdGluZyB0b3RhbCBjaG9sZXN0ZXJvbAp0cmVlX21vZGVsIDwtIHJwYXJ0KAogIHRvdENob2wgfiAuLCAKICBkYXRhID0gdHJhaW5fZGF0YSwKICBtZXRob2QgPSAiYW5vdmEiLCAgIyBSZWdyZXNzaW9uIHRyZWUKICBjb250cm9sID0gcnBhcnQuY29udHJvbCgKICAgIG1pbnNwbGl0ID0gMTAsICAgICAgICAgIyBNaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcmVxdWlyZWQgdG8gYXR0ZW1wdCBhIHNwbGl0CiAgICBtaW5idWNrZXQgPSA1LCAgICAgICAgICMgTWluaW11bSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRlcm1pbmFsIG5vZGVzCiAgICBjcCA9IDAuMDAyLCAgICAgICAgICAgICAjIENvbXBsZXhpdHkgcGFyYW1ldGVyIAogICAgbWF4ZGVwdGggPSA1ICAgICAgICAgICAjIE1heGltdW0gZGVwdGggb2YgdGhlIHRyZWUKICApCikKCiMgVmlzdWFsaXplIHRoZSB1bnBydW5lZCByZWdyZXNzaW9uIHRyZWUKcnBhcnQucGxvdCh0cmVlX21vZGVsLCBtYWluID0gIkluaXRpYWwgQ0FSVCBSZWdyZXNzaW9uIFRyZWUgZm9yIFRvdGFsIENob2xlc3Rlcm9sIikKCgpgYGAKCkZpZ3VyZSBMLiBJbml0aWFsIHJlZ3Jlc3Npb24gdHJlZSBiZWZvcmUgcHJ1bmluZy4gCgoKIyBIeXBlcnBhcmFtZXRlciBUdW5pbmcKClRvIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIHByZXZlbnQgb3ZlcmZpdHRpbmcsIHdlIHBlcmZvcm1lZCBoeXBlcnBhcmFtZXRlciB0dW5pbmcgb24gdGhlIENBUlQgcmVncmVzc2lvbiBtb2RlbCB1c2luZyAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24uIFNwZWNpZmljYWxseSwgd2UgdHVuZWQgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCksIHdoaWNoIGNvbnRyb2xzIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiB0cmVlIHNpemUgYW5kIHByZWRpY3RpdmUgYWNjdXJhY3kuCgoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIEJ1aWxkIHRoZSBpbml0aWFsIHJlZ3Jlc3Npb24gdHJlZSB3aXRoIGNyb3NzLXZhbGlkYXRpb24KdHJlZV9tb2RlbCA8LSBycGFydCgKICB0b3RDaG9sIH4gLiwgCiAgZGF0YSA9IHRyYWluX2RhdGEsCiAgbWV0aG9kID0gImFub3ZhIiwgICMgUmVncmVzc2lvbiB0cmVlCiAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woCiAgICBtaW5zcGxpdCA9IDEwLAogICAgbWluYnVja2V0ID0gNSwKICAgIGNwID0gMC4wMSwKICAgIG1heGRlcHRoID0gNSwKICAgIHh2YWwgPSAxMAogICkKKQoKIyBWaWV3IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZQpwYW5kZXIodHJlZV9tb2RlbCRjcHRhYmxlKQoKIyBQbG90IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB2cy4gZXJyb3IgKHhlcnJvcikKcGxvdGNwKHRyZWVfbW9kZWwpCgoKIyBDaG9vc2UgdGhlIG9wdGltYWwgY3AgYmFzZWQgb24geGVycm9yCm1pbl94ZXJyb3IgPC0gbWluKHRyZWVfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSkKYmVzdF9jcF9yb3cgPC0gd2hpY2gubWluKHRyZWVfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSkKYmVzdF9jcCA8LSB0cmVlX21vZGVsJGNwdGFibGVbYmVzdF9jcF9yb3csICJDUCJdCgojIFByaW50IHRoZSBiZXN0IGNwIHZhbHVlCmNhdCgiQmVzdCBDUDogIiwgcm91bmQoYmVzdF9jcCwgNCksICJcbiIpCgoKYGBgClRhYmxlIDQuIENyb3NzLXZhbGlkYXRpb24gcmVzdWx0cyBmb3IgZGlmZmVyZW50IGNvbXBsZXhpdHkgcGFyYW1ldGVycy4gCgpGaWd1cmUgTS4gUGxvdCBpbGx1c3RyYXRlcyB0aGUgZXJyb3IgY3VydmUuCgpBbHRob3VnaCB0aGUgY29tcGxleGl0eSBwbG90IHNob3dzIGFuIGVsYm93IGF0IGNwID0gMC4wNTEsIGluZGljYXRpbmcgYSBwb2ludCB3aGVyZSBmdXJ0aGVyIHNwbGl0cyBiZWdpbiB0byB5aWVsZCBkaW1pbmlzaGluZyByZXR1cm5zLCBjcm9zcy12YWxpZGF0aW9uIGlkZW50aWZpZWQgY3AgPSAwLjAxIGFzIHRoZSBvcHRpbWFsIHZhbHVlIGZvciBtaW5pbWl6aW5nIHByZWRpY3Rpb24gZXJyb3Igd2hpbGUgbWFpbnRhaW5pbmcgYSByZWFzb25hYmxlIG1vZGVsIGNvbXBsZXhpdHkuIE1vcmUgc3BlY2lmaWNhbGx5LCB0aGUgcGxvdCBpbmRpY2F0ZXMgdGhhdCBjcCA9IDAuMTIgaXMgYmVzdCB3aGVuIHVzaW5nIHRoZSAxLXN0YW5kYXJkIGVycm9yICgxLVNFKSBydWxlLgoKCiMjIFBydW5lZCBSZWdyZXNzaW9uIFRyZWU6IEJlc3QgQ1AgKDEtU0UpIAoKClRoaXMgY29kZSBzZWxlY3RzIGNvbXBsZXhpdHkgcGFyYW1ldGVycyAoY3ApIGZvciBwcnVuaW5nIHRoZSByZWdyZXNzaW9uIHRyZWUgYmFzZWQgb24gYm90aCB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0aW9uIGVycm9yIGFuZCB0aGUgMS1TRSBydWxlLiBJdCB0aGVuIHBydW5lcyB0aGUgdHJlZSBhbmQgbWFrZXMgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0LCBzYXZpbmcgdGhlIFItc3F1YXJlZCBhbmQgUk1TRSB2YWx1ZXMgZm9yIGxhdGVyIGNvbXBhcmlzb24gYWNyb3NzIHJlZ3Jlc3Npb24gbW9kZWxzLgoKYGBge3J9CiMgU3RlcCAxOiBJZGVudGlmeSB0aGUgbWluaW11bSB4ZXJyb3IgYW5kIGl0cyBjb3JyZXNwb25kaW5nIGNwIChmb3IgcHJ1bmluZyB3aXRoIG1pbmltdW0gZXJyb3IpCmNwLnRhYmxlIDwtIHRyZWVfbW9kZWwkY3B0YWJsZQptaW4ueGVycm9yIDwtIG1pbihjcC50YWJsZVssICJ4ZXJyb3IiXSkKbWluLmNwLnJvdyA8LSB3aGljaC5taW4oY3AudGFibGVbLCAieGVycm9yIl0pCm1pbi5jcCA8LSBjcC50YWJsZVttaW4uY3Aucm93LCAiQ1AiXQoKIyBTdGVwIDI6IENvbXB1dGUgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIHhlcnJvcgp4ZXJyb3Iuc3RkIDwtIGNwLnRhYmxlW21pbi5jcC5yb3csICJ4c3RkIl0KdGhyZXNob2xkIDwtIG1pbi54ZXJyb3IgKyB4ZXJyb3Iuc3RkCgojIFN0ZXAgMzogT3ZlcnJpZGUgYmVzdC5jcCBtYW51YWxseSB0byBnZXQgYSBtb3JlIGNvbXBsZXggdHJlZQpiZXN0LmNwIDwtIDAuMDEyICAjIGJhc2VkIG9uIDEtU0UgcnVsZSwgbWF4IENQIGZvciB4ZXJyb3IgPCAxCgojIFN0ZXAgNDogUHJ1bmUgdGhlIHRyZWUgdXNpbmcgYm90aCBtaW4uY3AgYW5kIG1hbnVhbGx5IHNwZWNpZmllZCBiZXN0LmNwCnBydW5lZC50cmVlLmJlc3QuY3AgPC0gcHJ1bmUodHJlZV9tb2RlbCwgY3AgPSBiZXN0LmNwKQpwcnVuZWQudHJlZS5taW4uY3AgPC0gcHJ1bmUodHJlZV9tb2RlbCwgY3AgPSBtaW4uY3ApCgojIFN0ZXAgNTogVmlzdWFsaXplIHRoZSBtYW51YWxseSBwcnVuZWQgdHJlZQpycGFydC5wbG90KHBydW5lZC50cmVlLmJlc3QuY3AsIAogICAgICAgICAgIG1haW4gPSBwYXN0ZSgiUHJ1bmVkIFRyZWUgQmVzdCBDUCAoMS1TRSk6IGNwID0iLCByb3VuZChiZXN0LmNwLCA1KSkpCgoKCiMgU3RlcCA2OiBNYWtlIHByZWRpY3Rpb25zIG9uIHRlc3QgZGF0YQpwcmVkLmJlc3QuY3AgPC0gcHJlZGljdChwcnVuZWQudHJlZS5iZXN0LmNwLCBuZXdkYXRhID0gdGVzdC5kYXRhKQpwcmVkLm1pbi5jcCAgPC0gcHJlZGljdChwcnVuZWQudHJlZS5taW4uY3AsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCgojIFN0ZXAgNzogU2F2ZSBSLXNxdWFyZWQgdmFsdWVzCnJzcXVhcmVkLmJlc3RjcCA8LSBjb3IodGVzdC5kYXRhJHRvdENob2wsIHByZWQuYmVzdC5jcCleMgoKIyBTYXZlIE1TRSBmb3IgYmVzdC5jcCAoMS1TRSBydWxlKQpybXNlLmJlc3RjcCA8LSBzcXJ0KG1lYW4oKHByZWQuYmVzdC5jcCAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKCgoKYGBgCgpGaWd1cmUgTi4gUHJ1bmVkIFRyZWUgQmVzdCBDUCB1c2luZyAxLVNFIFJ1bGUuIAoKCiAKCgoKIyMgUHJ1bmVkIFJlZ3Jlc3Npb24gVHJlZTogTWluaW11bSBDUAoKVGhlIGZvbGxvd2luZyB0cmVlIHJlcHJlc2VudHMgdGhlIHBydW5lZCByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoQ1ApIHRoYXQgbWluaW1pemVzIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IuIFRoaXMgdmVyc2lvbiBvZiB0aGUgdHJlZSAoTWluaW11bSBDUCkgYWltcyB0byBiYWxhbmNlIG1vZGVsIGNvbXBsZXhpdHkgYW5kIHByZWRpY3RpdmUgYWNjdXJhY3kgYnkgZml0dGluZyBhcyBjbG9zZWx5IGFzIHBvc3NpYmxlIHRvIHRoZSB0cmFpbmluZyBkYXRhIHdpdGhvdXQgb3ZlcmZpdHRpbmcuCgoKCmBgYHtyfQojIFZpc3VhbGl6ZSB0aGUgcHJ1bmVkIHRyZWUgdXNpbmcgdGhlIG1pbmltdW0gQ1AgdmFsdWUKcnBhcnQucGxvdChwcnVuZWQudHJlZS5taW4uY3AsCiAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJQcnVuZWQgUmVncmVzc2lvbiBUcmVlIChNaW4gQ1ApOiBjcCA9Iiwgcm91bmQobWluLmNwLCA0KSksCiAgICAgICAgICAgdHlwZSA9IDIsICAgICAgICAgICAgICAgICMgTGFiZWwgYWxsIG5vZGVzIHdpdGggcHJlZGljdGVkIHZhbHVlcwogICAgICAgICAgIGV4dHJhID0gMTAxLCAgICAgICAgICAgICAjIFNob3cgZml0dGVkIHZhbHVlcyBhbmQgJSBvZiBvYnNlcnZhdGlvbnMgYXQgZWFjaCBub2RlCiAgICAgICAgICAgdW5kZXIgPSBUUlVFLCAgICAgICAgICAgICMgRGlzcGxheSB0aGUgc3BsaXQgY29uZGl0aW9uIHVuZGVybmVhdGggdGhlIG5vZGUKICAgICAgICAgICBmYWNsZW4gPSAwLCAgICAgICAgICAgICAgIyBBdm9pZCBhYmJyZXZpYXRpbmcgZmFjdG9yIGxldmVscwogICAgICAgICAgIGNleCA9IDAuNywgICAgICAgICAgICAgICAjIFNocmluayB0ZXh0IGZvciBiZXR0ZXIgZml0CiAgICAgICAgICAgZmFsbGVuLmxlYXZlcyA9IFRSVUUsICAgICMgQmV0dGVyIGxheW91dCBmb3IgdGhlIGxlYXZlcyBhdCB0aGUgYm90dG9tCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAgICMgR3JlZW4tYmx1ZSBjb2xvciB0aGVtZSBmb3IgYm94ZXMKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiKSAgICAgIyBBZGQgc2hhZG93cyB0byBib3hlcyBmb3IgZGVwdGgKCiNyXjIgZm9yIG1pbi5jcApyc3F1YXJlZC5taW5jcCAgPC0gY29yKHRlc3QuZGF0YSR0b3RDaG9sLCBwcmVkLm1pbi5jcCleMgojU2F2ZSBNU0UgZm9yIG1pbi5jcCAobWluaW11bSB4ZXJyb3IpCnJtc2UubWluY3AgPC0gc3FydChtZWFuKChwcmVkLm1pbi5jcCAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKYGBgCkZpZ3VyZSBPLiBQcnVuZWQgUmVncmVzc2lvbiBUcmVlLCBNaW5pbXVtIENQLgoKVGhlIGZpbmFsIENBUlQgcmVncmVzc2lvbiBtb2RlbCBwcmVkaWN0aW5nIHRvdGFsIGNob2xlc3Rlcm9sIGlkZW50aWZpZWQgc2V4IGFuZCBjaWdhcmV0dGVzIHBlciBkYXkgYXMgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBjaG9sZXN0ZXJvbCBsZXZlbHMgaW4gdGhlIEZyYW1pbmdoYW0gZGF0YXNldCBhcmUgbW9zdCBlZmZlY3RpdmVseSBzZWdtZW50ZWQgYnkgZ2VuZGVyIGFuZCBzbW9raW5nIGJlaGF2aW9yLgoKCiMjIEJBR0dJTkcgQ0FSVCBSZWdyZXNzaW9uCgpUbyBmdXJ0aGVyIGltcHJvdmUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSwgd2UgbmV4dCBhcHBseSBCYWdnaW5nIChCb290c3RyYXAgQWdncmVnYXRpbmcpIHRvIENBUlQgcmVncmVzc2lvbi4gQmFnZ2luZyBidWlsZHMgbXVsdGlwbGUgdHJlZXMgb24gZGlmZmVyZW50IGJvb3RzdHJhcCBzYW1wbGVzIG9mIHRoZSBkYXRhIGFuZCBhdmVyYWdlcyB0aGVpciBwcmVkaWN0aW9ucywgaGVscGluZyB0byByZWR1Y2UgdmFyaWFuY2UgYW5kIGltcHJvdmUgbW9kZWwgc3RhYmlsaXR5IGNvbXBhcmVkIHRvIGEgc2luZ2xlIGRlY2lzaW9uIHRyZWUuCgpUbyBvcHRpbWl6ZSB0aGUgQmFnZ2luZyBDQVJUIHJlZ3Jlc3Npb24gbW9kZWwsIHdlIHBlcmZvcm0gYSBzeXN0ZW1hdGljIGh5cGVycGFyYW1ldGVyIHR1bmluZyBwcm9jZXNzLiBXZSBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMsIHNldCB1cCA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgYW5kIGV4cGxvcmUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiB0aGUgbnVtYmVyIG9mIGJhZ2dlZCB0cmVlcywgY29tcGxleGl0eSBwYXJhbWV0ZXJzIChjcCksIGFuZCBtYXhpbXVtIHRyZWUgZGVwdGhzIHRvIGlkZW50aWZ5IHRoZSBzZXR0aW5ncyB0aGF0IG1pbmltaXplIG91dC1vZi1iYWcgKE9PQikgZXJyb3IuCgoKYGBge3J9CgojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cwpzZXQuc2VlZCgxMjMpCnRyYWluLmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmhzJHRvdENob2wsIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW4uZGF0YSA8LSBmaHNbdHJhaW4uaW5kZXgsIF0KdGVzdC5kYXRhIDwtIGZoc1stdHJhaW4uaW5kZXgsIF0KCiMgU2V0IHVwIHRyYWluIGNvbnRyb2wgZm9yIGNyb3NzLXZhbGlkYXRpb24KY3RybCA8LSB0cmFpbkNvbnRyb2woCiAgbWV0aG9kID0gImN2IiwKICBudW1iZXIgPSA1LAogIHZlcmJvc2VJdGVyID0gVFJVRQopCgojIERlZmluZSBwYXJhbWV0ZXIgY29tYmluYXRpb25zIHRvIHRlc3QKbmJhZ2cudmFsdWVzIDwtIGMoMTAsIDI1LCA1MCkgICAgICMgbnVtYmVyIG9mIGJhZ2dlZCB0cmVlcwpjcC52YWx1ZXMgPC0gYygwLjAxLCAwLjA1LCAwLjEpICAgIyBjYW5kaWRhdGUgY3AgdmFsdWVzIGZvciBycGFydAptYXhkZXB0aC52YWx1ZXMgPC0gYyg1LCAxMCwgMjApICAgIyBtYXhpbXVtIGRlcHRoIG9mIHRoZSBjYW5kaWRhdGUgdHJlZQoKIyBDcmVhdGUgYW4gZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzCnJlc3VsdHMgPC0gZGF0YS5mcmFtZSgpCgojIE1vZGVsIHR1bmluZyBsb29wCmZvciAobmJhZ2cgaW4gbmJhZ2cudmFsdWVzKSB7CiAgZm9yIChjcCBpbiBjcC52YWx1ZXMpIHsKICAgIGZvciAobWF4ZGVwdGggaW4gbWF4ZGVwdGgudmFsdWVzKSB7CiAgICAgIHNldC5zZWVkKDEyMykKICAgICAgbW9kZWwgPC0gYmFnZ2luZygKICAgICAgICB0b3RDaG9sIH4gLiwKICAgICAgICBkYXRhID0gdHJhaW4uZGF0YSwKICAgICAgICBuYmFnZyA9IG5iYWdnLAogICAgICAgIGNvb2IgPSBUUlVFLAogICAgICAgIHRyQ29udHJvbCA9IGN0cmwsCiAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSBjcCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ZGVwdGggPSBtYXhkZXB0aCkKICAgICAgKQogICAgICAjIEdldCBPT0IgZXJyb3IgZnJvbSBlYWNoIGl0ZXJhdGlvbgogICAgICBvb2IuZXJyb3IgPC0gbW9kZWwkZXJyCiAgICAgICMgU3RvcmUgcmVzdWx0cwogICAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUobmJhZ2cgPSBuYmFnZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gY3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IG1heGRlcHRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb29iLmVycm9yID0gb29iLmVycm9yKSkKICAgIH0KICB9Cn0KCiMgRmluZCB0aGUgYmVzdCBjb21iaW5hdGlvbiB0aGF0IHlpZWxkcyB0aGUgbWluaW11bSBvdXQtb2YtYmFnIGVycm9yCmJlc3QucGFyYW1zIDwtIHJlc3VsdHNbd2hpY2gubWluKHJlc3VsdHMkb29iLmVycm9yKSwgXQpwYW5kZXIoYmVzdC5wYXJhbXMpCgoKCmBgYApUYWJsZSA1LiBCZXN0IGh5cGVycGFyYW1ldGVyIGNvbWJpbmF0aW9uIGZvciBCYWdnaW5nIENBUlQgcmVncmVzc2lvbiwgc2hvd2luZyA1MCB0cmVlcywgYSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIG9mIDAuMDEsIGFuZCBhIG1heGltdW0gdHJlZSBkZXB0aCBvZiA1LCByZXN1bHRpbmcgaW4gdGhlIGxvd2VzdCBvdXQtb2YtYmFnIGVycm9yICgzOS4wOSkuCgoKCgoKVGhlIGZpbmFsIEJhZ2dpbmcgQ0FSVCBtb2RlbCBpcyBmaXR0ZWQgdG8gdGhlIHRyYWluaW5nIGRhdGEgdXNpbmcgdGhlIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGZyb20gdGhlIHR1bmluZyBwcm9jZXNzLiBQcmVkaWN0aW9ucyBhcmUgdGhlbiBnZW5lcmF0ZWQgb24gdGhlIHRlc3Qgc2V0IGZvciBsYXRlciBldmFsdWF0aW9uLgoKYGBge3J9CgojIFJldHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMKc2V0LnNlZWQoMTIzKQpmaW5hbC5iYWdnaW5nLm1vZGVsIDwtIGJhZ2dpbmcoCiAgdG90Q2hvbCB+IC4sCiAgZGF0YSA9IHRyYWluLmRhdGEsCiAgbmJhZ2cgPSBiZXN0LnBhcmFtcyRuYmFnZywKICBjb29iID0gVFJVRSwKICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IGJlc3QucGFyYW1zJGNwLCBtYXhkZXB0aCA9IGJlc3QucGFyYW1zJG1heGRlcHRoKQopCgojIE1ha2UgcHJlZGljdGlvbnMgb24gdGVzdCBzZXQKcHJlZC5iYWdnaW5nIDwtIHByZWRpY3QoZmluYWwuYmFnZ2luZy5tb2RlbCwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKCiMgQ2FsY3VsYXRlIE1TRSBhbmQgUi1zcXVhcmVkCnJtc2UuYmFnZ2luZyA8LSBzcXJ0KG1lYW4oKHByZWQuYmFnZ2luZyAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKCnNzLnJlcyA8LSBzdW0oKHByZWQuYmFnZ2luZyAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKQpzcy50b3QgPC0gc3VtKChtZWFuKHRyYWluLmRhdGEkdG90Q2hvbCkgLSB0ZXN0LmRhdGEkdG90Q2hvbCleMikKcnNxLmJhZ2dpbmcgPC0gMSAtIChzcy5yZXMgLyBzcy50b3QpCgpgYGAKIAogCkluIHRoZSBuZXh0IHN0ZXAsIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggcHJlZGljdG9yIGluIHRoZSBmaW5hbCBCYWdnaW5nIG1vZGVsIGlzIGV2YWx1YXRlZC4gVGhpcyBhbmFseXNpcyBoZWxwcyBpZGVudGlmeSB0aGUgZmVhdHVyZXMgdGhhdCBoYXZlIHRoZSBtb3N0IHNpZ25pZmljYW50IGluZmx1ZW5jZSBvbiBwcmVkaWN0aW5nIHRoZSB0YXJnZXQgdmFyaWFibGUsIG9mZmVyaW5nIGluc2lnaHRzIGludG8gdGhlIG1vZGVs4oCZcyBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4gCiAKIApgYGB7cn0KICMgRXh0cmFjdCB2YXJpYWJsZSBpbXBvcnRhbmNlIHVzaW5nIGNhcmV0J3MgdmFySW1wIGZ1bmN0aW9uCnZhci5pbXAgPC0gdmFySW1wKGZpbmFsLmJhZ2dpbmcubW9kZWwpCgojIEN1c3RvbSBmdW5jdGlvbiB0byBnZXQgdmFyaWFibGUgaW1wb3J0YW5jZSBmcm9tIGJhZ2dpbmcgbW9kZWwKZ2V0LmJhZ2dpbmcuaW1wb3J0YW5jZSA8LSBmdW5jdGlvbihtb2RlbCkgewogICMgR2V0IGFsbCB0aGUgdHJlZXMgZnJvbSB0aGUgYmFnZ2luZyBtb2RlbAogIHRyZWVzIDwtIG1vZGVsJG10cmVlcwogIAogICMgSW5pdGlhbGl6ZSBpbXBvcnRhbmNlIHZlY3RvcgogIGltcCA8LSBudW1lcmljKGxlbmd0aCh0cmVlc1tbMV1dJGJ0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UpKQogIG5hbWVzKGltcCkgPC0gbmFtZXModHJlZXNbWzFdXSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKQogIAogICMgU3VtIGltcG9ydGFuY2UgYWNyb3NzIGFsbCB0cmVlcwogIGZvciAodHJlZSBpbiB0cmVlcykgewogICAgaW1wW25hbWVzKHRyZWUkYnRyZWUkdmFyaWFibGUuaW1wb3J0YW5jZSldIDwtIAogICAgICBpbXBbbmFtZXModHJlZSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKV0gKyAKICAgICAgdHJlZSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlCiAgfQogIAogICMgQXZlcmFnZSBpbXBvcnRhbmNlCiAgaW1wIDwtIGltcCAvIGxlbmd0aCh0cmVlcykKICByZXR1cm4oaW1wKQp9CgojIEdldCB2YXJpYWJsZSBpbXBvcnRhbmNlIGZyb20gdGhlIGZpbmFsIGJhZ2dpbmcgbW9kZWwKYmFnZ2luZ19pbXBvcnRhbmNlIDwtIGdldC5iYWdnaW5nLmltcG9ydGFuY2UoZmluYWwuYmFnZ2luZy5tb2RlbCkKCiMgRGlzcGxheSB2YXJpYWJsZSBpbXBvcnRhbmNlCiNwcmludChiYWdnaW5nX2ltcG9ydGFuY2UpCgojIEdldCBpbXBvcnRhbmNlCmltcG9ydGFuY2Uuc2NvcmVzIDwtIGdldC5iYWdnaW5nLmltcG9ydGFuY2UoZmluYWwuYmFnZ2luZy5tb2RlbCkKCgojIFNldCBjdXN0b20gbWFyZ2luczogYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0CnBhcihtYXIgPSBjKDUsIDgsIDQsIDIpKSAgIyBpbmNyZWFzZXMgbGVmdCBtYXJnaW4gZm9yIGxhYmVscwoKCiMgU29ydCBhbmQgcGxvdAppbXBvcnRhbmNlLnNjb3JlcyA8LSBzb3J0KGltcG9ydGFuY2Uuc2NvcmVzLCBkZWNyZWFzaW5nID0gVFJVRSkKYmFycGxvdChpbXBvcnRhbmNlLnNjb3JlcywgaG9yaXogPSBUUlVFLCBsYXMgPSAxLAogICAgICAgIG1haW4gPSAiVmFyaWFibGUgSW1wb3J0YW5jZSAtIEJhZ2dpbmcgKGlwcmVkKSIsCiAgICAgICAgeGxhYiA9ICJJbXBvcnRhbmNlIFNjb3JlIikKIAogCmBgYAoKRmlndXJlIFAuIFRoZSBiYXIgY2hhcnQgZGlzcGxheXMgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgZWFjaCBwcmVkaWN0b3IgdmFyaWFibGUgaW4gdGhlIGZpbmFsIEJhZ2dpbmcgbW9kZWwsIHdpdGggdGhlIHZhcmlhYmxlcyBvcmRlcmVkIGJ5IHRoZWlyIGNvbnRyaWJ1dGlvbiB0byB0aGUgbW9kZWzigJlzIHByZWRpY3Rpb25zLgoKCgojIyBSYW5kb20gRm9yZXN0IFJlZ3Jlc3Npb24KClJhbmRvbSBGb3Jlc3QgcmVncmVzc2lvbiBpcyBhIHBvd2VyZnVsIGVuc2VtYmxlIGxlYXJuaW5nIHRlY2huaXF1ZSB0aGF0IGFnZ3JlZ2F0ZXMgcHJlZGljdGlvbnMgZnJvbSBtdWx0aXBsZSBkZWNpc2lvbiB0cmVlcyB0byBpbXByb3ZlIGFjY3VyYWN5IGFuZCByb2J1c3RuZXNzLiBJbiB0aGUgcHJvY2VzcywgaHlwZXJwYXJhbWV0ZXJzIHN1Y2ggYXMgdGhlIG51bWJlciBvZiB0cmVlcywgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgdHJpZWQgYXQgZWFjaCBzcGxpdCwgYW5kIHRoZSBtaW5pbXVtIG5vZGUgc2l6ZSBwbGF5IGEgY3J1Y2lhbCByb2xlIGluIGRldGVybWluaW5nIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2UuIFRoZSBuZXh0IHN0ZXAgaW52b2x2ZXMgdHVuaW5nIHRoZXNlIGh5cGVycGFyYW1ldGVycyB0byBvcHRpbWl6ZSB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCBmb3IgcHJlZGljdGluZyB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiBhbmQgZXZhbHVhdGluZyB0aGUgcmVzdWx0aW5nIHBlcmZvcm1hbmNlLgoKCkh5cGVycGFyYW1ldGVyIHR1bmluZyBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwgaW52b2x2ZXMgdGVzdGluZyBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBtdHJ5LCBudHJlZSwgYW5kIG5vZGVzaXplLCB0byBpZGVudGlmeSB0aGUgb3B0aW1hbCBjb25maWd1cmF0aW9uIGJhc2VkIG9uIFJNU0UgcGVyZm9ybWFuY2UuCgpgYGB7cn0KIyBIeXBlcnBhcmFtZXRlciBUdW5pbmcgZm9yIFJhbmRvbSBGb3Jlc3QKdHVuZV9ncmlkIDwtIGV4cGFuZC5ncmlkKAogIG10cnkgPSBjKDIsIDQsIDYsIDgpLAogIG50cmVlID0gYygxMDAsIDIwMCksCiAgbm9kZXNpemUgPSBjKDEsIDUpCikKCnR1bmVfcmVzdWx0cyA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOm5yb3codHVuZV9ncmlkKSkgewogIHJmX3RlbXAgPC0gcmFuZG9tRm9yZXN0KAogICAgdG90Q2hvbCB+IC4sCiAgICBkYXRhID0gdHJhaW4uZGF0YSwKICAgIG10cnkgPSB0dW5lX2dyaWQkbXRyeVtpXSwKICAgIG50cmVlID0gdHVuZV9ncmlkJG50cmVlW2ldLAogICAgbm9kZXNpemUgPSB0dW5lX2dyaWQkbm9kZXNpemVbaV0KICApCiAgcHJlZHMgPC0gcHJlZGljdChyZl90ZW1wLCBuZXdkYXRhID0gdGVzdC5kYXRhKQogIHJtc2UgPC0gc3FydChtZWFuKCh0ZXN0LmRhdGEkdG90Q2hvbCAtIHByZWRzKV4yKSkKICB0dW5lX3Jlc3VsdHNbW2ldXSA8LSBjKAogICAgcm1zZSA9IHJtc2UsCiAgICBtdHJ5ID0gdHVuZV9ncmlkJG10cnlbaV0sCiAgICBudHJlZSA9IHR1bmVfZ3JpZCRudHJlZVtpXSwKICAgIG5vZGVzaXplID0gdHVuZV9ncmlkJG5vZGVzaXplW2ldCiAgKQp9CgojIENyZWF0ZSBkYXRhIGZyYW1lIGFuZCBmaW5kIGJlc3QgcGFyYW1ldGVycwp0dW5lX2RmIDwtIGFzLmRhdGEuZnJhbWUoZG8uY2FsbChyYmluZCwgdHVuZV9yZXN1bHRzKSkKYmVzdF9wYXJhbXMgPC0gdHVuZV9kZlt3aGljaC5taW4odHVuZV9kZiRybXNlKSwgXQoKCmBgYAoKCgpBZnRlciB0dW5pbmcgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwsIGEgc2VwYXJhdGUgQmFnZ2luZyBtb2RlbCBpcyBidWlsdCBieSBzZXR0aW5nIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBjb25zaWRlcmVkIGF0IGVhY2ggc3BsaXQgZXF1YWwgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBhdmFpbGFibGUgcHJlZGljdG9ycy4gVGhpcyBhcHByb2FjaCBlbXBoYXNpemVzIHJlZHVjaW5nIHZhcmlhbmNlIGJ5IGZ1bGx5IGdyb3dpbmcgZWFjaCB0cmVlIGFuZCBhdmVyYWdpbmcgdGhlaXIgcHJlZGljdGlvbnMgZm9yIG1vcmUgc3RhYmxlIHJlc3VsdHMuCgoKYGBge3J9CgpzZXQuc2VlZCgxMjMpCgojIEJhZ2dpbmcgbW9kZWw6IG10cnkgPSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdG9ycwpiYWdnaW5nX21vZGVsIDwtIHJhbmRvbUZvcmVzdCgKICB0b3RDaG9sIH4gLiwgCiAgZGF0YSA9IHRyYWluLmRhdGEsIAogIG10cnkgPSBuY29sKHRyYWluLmRhdGEpIC0gMSwgICMgRXhjbHVkZSB0YXJnZXQgdmFyaWFibGUKICBpbXBvcnRhbmNlID0gVFJVRQopCgojIFByZWRpY3Qgb24gdGVzdCBkYXRhCmJhZ2dpbmdfcHJlZCA8LSBwcmVkaWN0KGJhZ2dpbmdfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCgojIFBlcmZvcm1hbmNlOiBSTVNFCmJhZ2dpbmdfcm1zZSA8LSBzcXJ0KG1lYW4oKHRlc3QuZGF0YSR0b3RDaG9sIC0gYmFnZ2luZ19wcmVkKV4yKSkKI3ByaW50KHBhc3RlKCJCYWdnaW5nIFJNU0U6Iiwgcm91bmQoYmFnZ2luZ19ybXNlLCAyKSkpCgpgYGAKCgpVc2luZyB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMsIHRoZSBmaW5hbCBSYW5kb20gRm9yZXN0IG1vZGVsIGlzIHRyYWluZWQgb24gdGhlIGZ1bGwgdHJhaW5pbmcgc2V0LiBQcmVkaWN0aW9ucyBhcmUgbWFkZSBvbiB0aGUgdGVzdCBkYXRhLCBhbmQgbW9kZWwgcGVyZm9ybWFuY2UgaXMgYXNzZXNzZWQgdGhyb3VnaCBSTVNFIGFuZCBSLXNxdWFyZWQgdmFsdWVzLgoKCmBgYHtyfQojIEZpbmFsIFJhbmRvbSBGb3Jlc3QgTW9kZWwgd2l0aCBCZXN0IFBhcmFtcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpmaW5hbF9yZiA8LSByYW5kb21Gb3Jlc3QoCiAgdG90Q2hvbCB+IC4sIAogIGRhdGEgPSB0cmFpbi5kYXRhLAogIG10cnkgPSBiZXN0X3BhcmFtcyRtdHJ5LAogIG50cmVlID0gYmVzdF9wYXJhbXMkbnRyZWUsCiAgbm9kZXNpemUgPSBiZXN0X3BhcmFtcyRub2Rlc2l6ZSwKICBpbXBvcnRhbmNlID0gVFJVRQopCgojIFByZWRpY3Qgb24gdGVzdCBkYXRhCnJmX3ByZWQgPC0gcHJlZGljdChmaW5hbF9yZiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKCiMgQ2FsY3VsYXRlIE1TRQpybXNlLnJmb3Jlc3QgPC0gc3FydChtZWFuKCh0ZXN0LmRhdGEkdG90Q2hvbCAtIHJmX3ByZWQpXjIpKQoKIyBDYWxjdWxhdGUgUi1zcXVhcmVkCnNzLnJlcyA8LSBzdW0oKHRlc3QuZGF0YSR0b3RDaG9sIC0gcmZfcHJlZCleMikKc3MudG90IDwtIHN1bSgobWVhbih0cmFpbi5kYXRhJHRvdENob2wpIC0gdGVzdC5kYXRhJHRvdENob2wpXjIpCnJzcS5yZm9yZXN0IDwtIDEgLSAoc3MucmVzIC8gc3MudG90KQoKcHJpbnQoZmluYWxfcmYpCgpgYGAKVGFibGUgNi4gU3VtbWFyeSBvZiB0aGUgZmluYWwgUmFuZG9tIEZvcmVzdCBtb2RlbCwgZGlzcGxheWluZyB0aGUgbnVtYmVyIG9mIHRyZWVzLCBzZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMsIG91dC1vZi1iYWcgKE9PQikgZXJyb3IgZXN0aW1hdGUsIGFuZCB2YXJpYWJsZSBpbXBvcnRhbmNlIG1lYXN1cmVzLgoKCgpWYXJpYWJsZSBpbXBvcnRhbmNlIGlzIGV2YWx1YXRlZCBmb3IgdGhlIGZpbmFsIFJhbmRvbSBGb3Jlc3QgbW9kZWwgdG8gaWRlbnRpZnkgdGhlIHByZWRpY3RvcnMgdGhhdCBjb250cmlidXRlIG1vc3QgdG8gdGhlIHByZWRpY3Rpb24gb2YgdG90YWwgY2hvbGVzdGVyb2wuCgpgYGB7cn0KIyBWYXJpYWJsZSBpbXBvcnRhbmNlCmltcG9ydGFuY2UoZmluYWxfcmYpCnZhckltcFBsb3QoZmluYWxfcmYsIG1haW4gPSAiUmFuZG9tIEZvcmVzdCBWYXJpYWJsZSBJbXBvcnRhbmNlIikKCmBgYApUYWJsZSA3LiBWYXJpYWJsZSBpbXBvcnRhbmNlIHRhYmxlLiAKCkZpZ3VyZSBRLiBWYXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QuIAoKVGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgdGFibGUgcHJlc2VudHMgdHdvIGtleSBtZXRyaWNzOiAlSW5jTVNFLCB3aGljaCBtZWFzdXJlcyB0aGUgaW5jcmVhc2UgaW4gbWVhbiBzcXVhcmVkIGVycm9yIHdoZW4gYSB2YXJpYWJsZSBpcyByYW5kb21seSBwZXJtdXRlZCwgYW5kIEluY05vZGVQdXJpdHksIHdoaWNoIHJlZmxlY3RzIHRoZSB0b3RhbCBkZWNyZWFzZSBpbiBub2RlIGltcHVyaXR5IGR1ZSB0byBzcGxpdHMgb24gZWFjaCB2YXJpYWJsZS4gSGlnaGVyIHZhbHVlcyBpbiBlaXRoZXIgbWV0cmljIGluZGljYXRlIGdyZWF0ZXIgaW5mbHVlbmNlIG9uIHRoZSBtb2RlbOKAmXMgcHJlZGljdGl2ZSBhY2N1cmFjeS4gCgpUaGUgYWNjb21wYW55aW5nIFJhbmRvbSBGb3Jlc3QgVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90IHZpc3VhbGx5IHJhbmtzIHRoZSBwcmVkaWN0b3JzIGJhc2VkIG9uIHRoZXNlIHNjb3JlcywgcHJvdmlkaW5nIGFuIGludHVpdGl2ZSB3YXkgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wYWN0ZnVsIGZlYXR1cmVzIGluIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2UuCgoKCgpUaGUgdGFibGUgb2YgdmFyaWFibGUgaW1wb3J0YW5jZSBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwgc2hvd3MgdHdvIGtleSBtZXRyaWNzOiAlSW5jTVNFLCB3aGljaCBpbmRpY2F0ZXMgaG93IG11Y2ggdGhlIG1lYW4gc3F1YXJlZCBlcnJvciBpbmNyZWFzZXMgd2hlbiBlYWNoIHZhcmlhYmxlIGlzIHBlcm11dGVkLCBhbmQgSW5jTm9kZVB1cml0eSwgd2hpY2ggcmVmbGVjdHMgdGhlIHRvdGFsIGRlY3JlYXNlIGluIG5vZGUgaW1wdXJpdHkgZHVlIHRvIGVhY2ggdmFyaWFibGUuIFRvIHZpc3VhbGl6ZSB0aGVzZSByZXN1bHRzLCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90IHJhbmtzIHRoZSBmZWF0dXJlcyBiYXNlZCBvbiB0aGVpciBpbXBhY3QsIHByb3ZpZGluZyBhbiBpbnR1aXRpdmUgdmlldyBvZiB3aGljaCB2YXJpYWJsZXMgYXJlIG1vc3QgaW5mbHVlbnRpYWwgaW4gcHJlZGljdGluZyB0aGUgdGFyZ2V0LgoKCgojIyBDb21wYXJpc29uOiBNb2RlbCBQZXJmb3JtYW5jZQoKSW4gdGhpcyBhbmFseXNpcywgdmFyaW91cyByZWdyZXNzaW9uIG1vZGVscyBhcmUgZXZhbHVhdGVkIHRvIHByZWRpY3QgdG90YWwgY2hvbGVzdGVyb2wgKHRvdENob2wpIHVzaW5nIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IGRhdGFzZXQuIFRoZSBtb2RlbHPigJkgcGVyZm9ybWFuY2UgaXMgYXNzZXNzZWQgdGhyb3VnaCBrZXkgbWV0cmljcyBzdWNoIGFzIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKSBhbmQgUi1zcXVhcmVkLCB3aXRoIHRoZSBnb2FsIG9mIGRldGVybWluaW5nIHdoaWNoIG1vZGVsIG1vc3QgYWNjdXJhdGVseSBjYXB0dXJlcyB0aGUgdW5kZXJseWluZyBwYXR0ZXJucyBpbiB0aGUgZGF0YS4KCmBgYHtyfQoKIy0tLSBBc3N1bWVzIHRoZXNlIHZhbHVlcyBhcmUgYWxyZWFkeSBjb21wdXRlZCBhbmQgc3RvcmVkIC0tLQojIG1zZS5iZXN0Y3AsIG1zZS5taW5jcAojIHJzcXVhcmVkLmJlc3RjcCwgcnNxdWFyZWQubWluY3AKIyBtc2UuYmFnZ2luZywgcnNxLmJhZ2dpbmcKIyBtc2UucmZvcmVzdCwgcnNxLnJmb3Jlc3QKCiMgQ2FsY3VsYXRlIExTRSBtb2RlbCAoU3RlcHdpc2UgQUlDKQptb2RlbC5sc2UgPC0gc3RlcChsbSh0b3RDaG9sIH4gLiwgZGF0YSA9IHRyYWluLmRhdGEpLCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gRkFMU0UpCnByZWQubHNlIDwtIHByZWRpY3QobW9kZWwubHNlLCBuZXdkYXRhID0gdGVzdC5kYXRhKQoKIyBDb21wdXRlIG1ldHJpY3MgZm9yIExTRSBtb2RlbApybXNlLmxzZSA8LSBzcXJ0KG1lYW4oKHRlc3QuZGF0YSR0b3RDaG9sIC0gcHJlZC5sc2UpXjIpKQpzcy5yZXMubHNlIDwtIHN1bSgodGVzdC5kYXRhJHRvdENob2wgLSBwcmVkLmxzZSleMikKc3MudG90LmxzZSA8LSBzdW0oKG1lYW4odHJhaW4uZGF0YSR0b3RDaG9sKSAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKQpyc3EubHNlIDwtIDEgLSAoc3MucmVzLmxzZSAvIHNzLnRvdC5sc2UpCgojLS0tIENyZWF0ZSBzdW1tYXJ5IHRhYmxlIC0tLQptb2RlbF9yZXN1bHRzIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWwgPSBjKCJQcnVuZWQgVHJlZSAobWluIGNwKSIsIAogICAgICAgICAgICAiUHJ1bmVkIFRyZWUgKDEtU0UgY3ApIiwgCiAgICAgICAgICAgICJCYWdnaW5nIiwgCiAgICAgICAgICAgICJSYW5kb20gRm9yZXN0IiwgCiAgICAgICAgICAgICJTdGVwd2lzZSBBSUMgKExTRSkiKSwKICBSTVNFID0gYyhybXNlLm1pbmNwLCAKICAgICAgICAgIHJtc2UuYmVzdGNwLCAKICAgICAgICAgIHJtc2UuYmFnZ2luZywgCiAgICAgICAgICBybXNlLnJmb3Jlc3QsIAogICAgICAgICAgcm1zZS5sc2UpLAogIFJfc3F1YXJlZCA9IGMocnNxdWFyZWQubWluY3AsIAogICAgICAgICAgICAgICAgcnNxdWFyZWQuYmVzdGNwLCAKICAgICAgICAgICAgICAgIHJzcS5iYWdnaW5nLCAKICAgICAgICAgICAgICAgIHJzcS5yZm9yZXN0LCAKICAgICAgICAgICAgICAgIHJzcS5sc2UpCikKCiMgVmlldyBjb21wYXJpc29uCnByaW50KG1vZGVsX3Jlc3VsdHMpCgpgYGAKClRhYmxlIDguIFJNU0UgYW5kIFItc3F1YXJlZCB2YWx1ZXMgZm9yIHRoZSBmb3VyIHRyZWUgbW9kZWxzLCB3aXRoIFN0ZXB3aXNlIEFJQyAoTFNFKSBhcyB0aGUgYmFzZWxpbmUgZm9yIGNvbXBhcmlzb24uIAoKClJhbmRvbSBGb3Jlc3Qgb3V0cGVyZm9ybWVkIHRoZSBvdGhlciByZWdyZXNzaW9uIG1vZGVscywgYWNoaWV2aW5nIHRoZSBoaWdoZXN0IFItc3F1YXJlZCBhbmQgdGhlIGxvd2VzdCBSTVNFLCBpbmRpY2F0aW5nIGl0cyBzdXBlcmlvciBhYmlsaXR5IHRvIGNhcHR1cmUgdGhlIHJlbGF0aW9uc2hpcHMgd2l0aGluIHRoZSBkYXRhLiBQcnVuZWQgVHJlZSAobWluIGNwKSBmb2xsb3dlZCBjbG9zZWx5IGJlaGluZCwgc2hvd2luZyBjb21wZXRpdGl2ZSBwZXJmb3JtYW5jZSBidXQgc2xpZ2h0bHkgbG93ZXIgcHJlZGljdGl2ZSBhY2N1cmFjeSBjb21wYXJlZCB0byBSYW5kb20gRm9yZXN0LgpjbGVhbmVkIG9mIHJlZHVuZGFuY3kgYnV0IG1ldHJpYyBhcmUgZGlmZmVyZW50IHRoYW4gbXkgY29kZQoKCgoKIyBDQVJUIENMQVNTSUZJQ0FUSU9OCgpDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgY2xhc3NpZmljYXRpb24gY29uc3RydWN0cyBhIGRlY2lzaW9uIHRyZWUgdGhhdCBzcGxpdHMgdGhlIGRhdGEgaW50byBpbmNyZWFzaW5nbHkgaG9tb2dlbmVvdXMgZ3JvdXBzIGJhc2VkIG9uIGEgY2F0ZWdvcmljYWwgb3V0Y29tZSwgc3VjaCBhcyB0aGUgcHJlc2VuY2Ugb3IgYWJzZW5jZSBvZiAxMC15ZWFyIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UgKFRlblllYXJDSEQpIGluIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IGRhdGFzZXQuIFRoaXMgYXBwcm9hY2ggaWRlbnRpZmllcyB0aGUgbW9zdCBpbXBvcnRhbnQgcHJlZGljdG9ycyBhbmQgb3B0aW1hbCBzcGxpdCBwb2ludHMgdG8gYmVzdCBjbGFzc2lmeSBpbmRpdmlkdWFscyBhY2NvcmRpbmcgdG8gdGhlaXIgcmlzayBvZiBkZXZlbG9waW5nIENIRCB3aXRoaW4gdGVuIHllYXJzLiBUaGUga2V5IGNvbXBvbmVudHMgZm9yIHRoaXMgYW5hbHlzaXMgYXJlIGxpc3RlZCBiZWxvdy4KCjEpIFNwbGl0dGluZyBDcml0ZXJpb246IFVzZXMgbWVhc3VyZXMgbGlrZSBHaW5pIGltcHVyaXR5IG9yIGVudHJvcHkgdG8gY2hvb3NlIHRoZSBiZXN0IHZhcmlhYmxlIGFuZCBzcGxpdCBwb2ludC4KMikgU3RvcHBpbmcgUnVsZXM6IFBhcmFtZXRlcnMgc3VjaCBhcyBtaW5zcGxpdCwgbWluYnVja2V0LCBhbmQgbWF4ZGVwdGggY29udHJvbCB0cmVlIGdyb3d0aCBhbmQgcHJldmVudCBvdmVyZml0dGluZy4KMykgUHJ1bmluZzogUmVkdWNlcyB0cmVlIGNvbXBsZXhpdHkgdXNpbmcgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgYW5kIGNyb3NzLXZhbGlkYXRpb24gdG8gc2VsZWN0IGFuIG9wdGltYWwgc3VidHJlZS4KNCkgUHJlZGljdGlvbiBhbmQgRXZhbHVhdGlvbjogVGhlIGZpbmFsIHRyZWUgaXMgdXNlZCB0byBwcmVkaWN0IG91dGNvbWVzIG9uIHRlc3QgZGF0YSwgdHlwaWNhbGx5IGV2YWx1YXRlZCB1c2luZyBhY2N1cmFjeSwgc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5LCBhbmQgQVVDLgoKCiMjIEluaXRpYWwgQ0FSVCBDbGFzc2lmaWNhdGlvbgoKVGhlIENBUlQgY2xhc3NpZmljYXRpb24gbW9kZWwgZm9yIHByZWRpY3RpbmcgVGVuWWVhckNIRCBpcyBidWlsdCB1c2luZyBhIHN1YnNldCBvZiB0aGUgRnJhbWluZ2hhbSBIZWFydCBTdHVkeSBkYXRhc2V0LiBUaGUgdHJlZSBpcyBjb25zdHJ1Y3RlZCB3aXRoIHNwZWNpZmljIHBhcmFtZXRlcnMgc3VjaCBhcyBsaW1pdGluZyB0aGUgZGVwdGggb2YgdGhlIHRyZWUgYW5kIGVuc3VyaW5nIGEgbWluaW11bSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBzcGxpdHMuIFRoZSByZXN1bHRpbmcgbW9kZWwgcmVwcmVzZW50cyB0aGUgZnVsbCwgdW5wcnVuZWQgdHJlZSwgd2hpY2ggaXMgdGhlbiB2aXN1YWxpemVkIHRvIHNob3cgdGhlIGNsYXNzIHByb2JhYmlsaXRpZXMgYW5kIGxhYmVscyBhdCBlYWNoIG5vZGUsIHByb3ZpZGluZyBpbnNpZ2h0cyBpbnRvIGhvdyBkaWZmZXJlbnQgdmFyaWFibGVzIGNvbnRyaWJ1dGUgdG8gdGhlIHByZWRpY3Rpb24gb2YgMTAteWVhciBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIHJpc2suCgpgYGB7cn0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEyMykKCiMgU3BsaXQgZGF0YSBpbnRvIHRyYWluaW5nICg3MCUpIGFuZCB0ZXN0aW5nICgzMCUpIHNldHMKdHJhaW5faW5kZXggPC0gc2FtcGxlKDE6bnJvdyhmaHMpLCBzaXplID0gMC43ICogbnJvdyhmaHMpKQp0cmFpbl9kYXRhIDwtIGZoc1t0cmFpbl9pbmRleCwgXQp0ZXN0X2RhdGEgPC0gZmhzWy10cmFpbl9pbmRleCwgXQoKIyBCdWlsZCBpbml0aWFsIENBUlQgY2xhc3NpZmljYXRpb24gbW9kZWwgKHByZWRpY3RpbmcgVGVuWWVhckNIRCkKY2xhc3NfbW9kZWwgPC0gcnBhcnQoCiAgVGVuWWVhckNIRCB+IC4sIAogIGRhdGEgPSB0cmFpbl9kYXRhLAogIG1ldGhvZCA9ICJjbGFzcyIsICAjIENsYXNzaWZpY2F0aW9uIHRyZWUKICBjb250cm9sID0gcnBhcnQuY29udHJvbCgKICAgIG1pbnNwbGl0ID0gMTAsCiAgICBtaW5idWNrZXQgPSA1LAogICAgY3AgPSAwLjAwMDEsCiAgICBtYXhkZXB0aCA9IDUKICApCikKCiMgVmlzdWFsaXplIHRoZSBjbGFzc2lmaWNhdGlvbiB0cmVlCnJwYXJ0LnBsb3QoY2xhc3NfbW9kZWwsIAogICAgICAgICAgIGV4dHJhID0gMTA0LCAgICAgICAgICAgICAjIFNob3cgcHJvYmFiaWxpdGllcyBhbmQgY2xhc3MgbGFiZWxzCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAgICMgR3JlZW4gdG8gYmx1ZSBncmFkaWVudAogICAgICAgICAgIGJyYW5jaC5sdHkgPSAxLCAKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiLCAKICAgICAgICAgICBubiA9IFRSVUUsICAgICAgICAgICAgICAgIyBTaG93IG5vZGUgbnVtYmVycwogICAgICAgICAgIG1haW4gPSAiSW5pdGlhbCBDQVJUIENsYXNzaWZpY2F0aW9uIFRyZWUgZm9yIFRlblllYXJDSEQiKQoKYGBgCgpGaWd1cmUgUi4gSW5pdGlhbCBDQVJUIERlY2lzaW9uIFRyZWUuIAoKCiMjIFR1bmluZwoKClRoaXMgY29kZSBldmFsdWF0ZXMgdGhlIHR1bmluZyBvZiBhIGNsYXNzaWZpY2F0aW9uIHRyZWUgbW9kZWwgYnkgZXhhbWluaW5nIHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHZhbHVlcyBmcm9tIHRoZSBtb2RlbOKAmXMgY29zdC1jb21wbGV4aXR5IHRhYmxlIChjcHRhYmxlKS4gSXQgaWRlbnRpZmllcyB0aGUgY3AgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0aW9uIGVycm9yICh4ZXJyb3IpIGFuZCB0aGUgY3Agd2l0aGluIHRoZSAxLXN0YW5kYXJkIGVycm9yICgxLVNFKSByYW5nZSwgd2hpY2ggaGVscHMgc2VsZWN0IHRoZSBzaW1wbGVzdCB0cmVlIHRoYXQgc3RpbGwgcHJvdmlkZXMgb3B0aW1hbCBwZXJmb3JtYW5jZS4gQnkgdHVuaW5nIHRoZSBjcCwgd2UgYWltIHRvIGJhbGFuY2UgbW9kZWwgY29tcGxleGl0eSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeSwgd2l0aCB0aGUgMS1TRSBydWxlIHNlcnZpbmcgYXMgYSBjb25zZXJ2YXRpdmUgYXBwcm9hY2ggdG8gcHJldmVudCBvdmVyZml0dGluZy4gVGhlIG91dHB1dCBwcm92aWRlcyBib3RoIHRoZSBtaW5pbXVtIGNwIGFuZCB0aGUgY3Agd2l0aGluIHRoZSAxLVNFIHJhbmdlLCBvZmZlcmluZyB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiBhIG1vcmUgY29tcGxleCBtb2RlbCBhbmQgYSBzaW1wbGVyLCBwb3RlbnRpYWxseSBtb3JlIGdlbmVyYWxpemFibGUgb25lLgoKCgoKYGBge3J9CgpwYW5kZXIoY2xhc3NfbW9kZWwkY3B0YWJsZSkKCm1pbi5yb3cgPC0gd2hpY2gubWluKGNsYXNzX21vZGVsJGNwdGFibGVbLCAieGVycm9yIl0pCm1pbi5jcCA8LSBjbGFzc19tb2RlbCRjcHRhYmxlW21pbi5yb3csICJDUCJdCnNlIDwtIGNsYXNzX21vZGVsJGNwdGFibGVbbWluLnJvdywgInhzdGQiXQp4ZXJyb3IuY3V0b2ZmIDwtIGNsYXNzX21vZGVsJGNwdGFibGVbbWluLnJvdywgInhlcnJvciJdICsgc2UKCiMgTm93IGZpbmQgdGhlIHNpbXBsZXN0IChsYXJnZXN0IGNwKSB0cmVlIHdpdGhpbiAxLVNFIHJhbmdlCmNwLjFTRSA8LSBtYXgoY2xhc3NfbW9kZWwkY3B0YWJsZVtjbGFzc19tb2RlbCRjcHRhYmxlWywgInhlcnJvciJdIDw9IHhlcnJvci5jdXRvZmYsICJDUCJdKQoKY2F0KCJtaW4uY3A6IiwgbWluLmNwLCAiXG4iKQpjYXQoImNwLjFTRToiLCBjcC4xU0UsICJcbiIpCgoKIyBDaGVjayB0aGUgbnVtYmVyIG9mIHRlcm1pbmFsIG5vZGVzIChsZWF2ZXMpIGluIGJvdGggdHJlZXMKI2NhdCgiVGVybWluYWwgbm9kZXMgZm9yIHBydW5lZC50cmVlLjFTRToiLCBsZW5ndGgocHJ1bmVkLnRyZWUuMVNFJGZyYW1lJHZhcltwcnVuZWQudHJlZS4xU0UkZnJhbWUkdmFyID09ICI8bGVhZj4iXSksICJcbiIpCiNjYXQoIlRlcm1pbmFsIG5vZGVzIGZvciBwcnVuZWQudHJlZS5taW46IiwgbGVuZ3RoKHBydW5lZC50cmVlLm1pbiRmcmFtZSR2YXJbcHJ1bmVkLnRyZWUubWluJGZyYW1lJHZhciA9PSAiPGxlYWY+Il0pLCAiXG4iKQoKIyBDb21wYXJlIHNwbGl0cyBiZXR3ZWVuIHRoZSB0d28gdHJlZXMgKHN0cnVjdHVyZSkKI3ByaW50KHBydW5lZC50cmVlLjFTRSkKI3ByaW50KHBydW5lZC50cmVlLm1pbikKCiMgUHJpbnQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgcHJ1bmVkIHRyZWVzIHRvIGluc3BlY3QgZGlmZmVyZW5jZXMKI3N1bW1hcnkocHJ1bmVkLnRyZWUuMVNFKQojc3VtbWFyeShwcnVuZWQudHJlZS5taW4pCgoKYGBgCgpUYWJsZSA5LiBDb21wbGV4aXR5IFBhcmFtZXRlciBUYWJsZS4KClRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvciBvY2N1cnJlZCBhdCBhIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgdmFsdWUgb2YgMC4wMTAyLiBVc2luZyB0aGUgMS1TRSBydWxlLCB0aGUgc2ltcGxlc3QgdHJlZSB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIGVycm9yIGFsc28gY29ycmVzcG9uZHMgdG8gdGhlIHNhbWUgY3AgdmFsdWUgb2YgMC4wMTAyLiBUaGlzIHN1Z2dlc3RzIHRoYXQgbm8gZnVydGhlciBzaW1wbGlmaWNhdGlvbiBpcyBuZWNlc3NhcnksIGFuZCB0aGUgdHJlZSB3aXRoIGNwID0gMC4wMTAyIGlzIGJvdGggdGhlIG1vc3QgYWNjdXJhdGUgYW5kIHRoZSBtb3N0IHBhcnNpbW9uaW91cyBjaG9pY2UuCgoKVGhlIGZvbGxvd2luZyBwbG90IGRpc3BsYXlzIHRoZSBjcm9zcy12YWxpZGF0aW9uIHJlc3VsdHMgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiB0cmVlIG1vZGVsLCBpbGx1c3RyYXRpbmcgaG93IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIGluZmx1ZW5jZXMgdGhlIG1vZGVs4oCZcyBjcm9zcy12YWxpZGF0aW9uIGVycm9yLgoKCmBgYHtyfQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cwpwbG90Y3AoY2xhc3NfbW9kZWwpCgpgYGAKCkZpZ3VyZSBTLiBDQVJUIENvbXBsZXhpdHkgUGFyYW1ldGVyIChjcCkgUGxvdC4gCgpUaGUgY3Jvc3MtdmFsaWRhdGlvbiBwbG90IHNob3dzIHRoYXQgdGhlIGxvd2VzdCBlcnJvciBvY2N1cnMgYXQgYSB0cmVlIHNpemUgb2YgNSwgY29ycmVzcG9uZGluZyB0byBhIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgb2YgMC4wMDYyLCBzdWdnZXN0aW5nIHRoYXQgdGhpcyB0cmVlIHNpemUgb2ZmZXJzIHRoZSBiZXN0IGJhbGFuY2UgYmV0d2VlbiBtb2RlbCBjb21wbGV4aXR5IGFuZCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLgoKCiMjIENBUlQgQ2xhc3NpZmljYXRpb24gKDEtU0UgUnVsZSkKCgpUaGUgcHJ1bmVkIENBUlQgY2xhc3NpZmljYXRpb24gdHJlZSB1c2luZyB0aGUgMS1TRSBydWxlIGlzIGEgbWV0aG9kIGZvciBzaW1wbGlmeWluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCBieSByZWR1Y2luZyBpdHMgY29tcGxleGl0eSB3aGlsZSBtYWludGFpbmluZyBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiBUaGlzIGFwcHJvYWNoIGludm9sdmVzIHNlbGVjdGluZyB0aGUgc21hbGxlc3QgdHJlZSB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IsIGVuc3VyaW5nIGEgYmFsYW5jZSBiZXR3ZWVuIG1vZGVsIHNpbXBsaWNpdHkgYW5kIGFjY3VyYWN5LiBVc2luZyB0aGUgcGxvdCwgYSBjcCBvZiAwLjAwNjIgaXMgdXNlZCBmb3IgMS1TRSBSdWxlLiAKCmBgYHtyfQoKIyAxLiBGaW5kIHRoZSBvcHRpbWFsIGNwIHZhbHVlIHRoYXQgbWluaW1pemVzIGNyb3NzLXZhbGlkYXRlZCBlcnJvcgptaW4uY3AgPC0gY2xhc3NfbW9kZWwkY3B0YWJsZVt3aGljaC5taW4oY2xhc3NfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSksICJDUCJdCgojIDIuIE1hbnVhbGx5IHNwZWNpZnkgdGhlIDEtU0UgY3AgdmFsdWUgKHlvdSBhbHJlYWR5IGZvdW5kIHRoaXMgZWFybGllcikKY3AuMVNFIDwtIDAuMDA2MgoKIyAzLiBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgMS1TRSBjcCB2YWx1ZQpwcnVuZWQudHJlZS4xU0UgPC0gcHJ1bmUoY2xhc3NfbW9kZWwsIGNwID0gY3AuMVNFKQoKIyA0LiBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlICgxLVNFIHJ1bGUgdmVyc2lvbikKcnBhcnQucGxvdChwcnVuZWQudHJlZS4xU0UsIAogICAgICAgICAgIGV4dHJhID0gMTA0LCAgICAgICAgICAgICAgIyBTaG93IHByZWRpY3RlZCBjbGFzcyBhbmQgcHJvYmFiaWxpdHkKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgICAgICMgR3JlZW4gdG8gYmx1ZSBjb2xvciBzY2hlbWUKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFLCAKICAgICAgICAgICBtYWluID0gIlBydW5lZCBDQVJUIENsYXNzaWZpY2F0aW9uIFRyZWUgKDEtU0UgUnVsZSkiKQoKIyA1LiBHZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgcHJ1bmVkIHRyZWUgKDEtU0UgcnVsZSkKcHJlZC5wcm9iLjFTRSA8LSBwcmVkaWN0KHBydW5lZC50cmVlLjFTRSwgdGVzdF9kYXRhLCB0eXBlID0gInByb2IiKVssIDJdCgojIDYuIENyZWF0ZSBST0MgY3VydmUgZm9yIHBydW5lZCB0cmVlICgxLVNFIHJ1bGUpCnJvYy50cmVlLjFTRSA8LSBwUk9DOjpyb2ModGVzdF9kYXRhJFRlblllYXJDSEQsIHByZWQucHJvYi4xU0UpCgojIDcuIEFjY2VzcyB0aGUgQVVDIGRpcmVjdGx5IGZyb20gdGhlIFJPQyBvYmplY3QKYXVjLnRyZWUuMVNFIDwtIHJvYy50cmVlLjFTRSRhdWMKCgojIDguIE9wdGlvbmFsbHksIHByaW50IHRoZSBBVUMKI3ByaW50KHBhc3RlKCJBVUMgZm9yIHBydW5lZCB0cmVlICgxLVNFKToiLCByb3VuZChhdWMudHJlZS4xU0UsIDQpKSkKCgpgYGAKCkZpZ3VyZSBULiBQcnVuZWQgQ0FSVCBDbGFzc2lmaWNhdGlvbiB0cmVlIHVzaW5nIDEtU0UgUnVsZS4KCgoKCiMjIENBUlQgQ2xhc3NpZmljYXRpb24gVHJlZSAoTWluIENQKQoKVGhlIHBydW5lZCBDQVJUIGNsYXNzaWZpY2F0aW9uIHRyZWUgaXMgdmlzdWFsaXplZCB1c2luZyB0aGUgbWluaW11bSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHZhbHVlLCB3aGljaCBoZWxwcyBpbiByZWR1Y2luZyBtb2RlbCBjb21wbGV4aXR5IHdoaWxlIHJldGFpbmluZyBwcmVkaWN0aXZlIGFjY3VyYWN5LiBUaGlzIHZpc3VhbGl6YXRpb24gaGlnaGxpZ2h0cyB0aGUgY2xhc3MgcHJvYmFiaWxpdGllcyBhbmQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgYXQgZWFjaCBub2RlLCBwcm92aWRpbmcgYSBjbGVhcmVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1vZGVs4oCZcyBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4KCmBgYHtyfQoKIyBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgY3AgdGhhdCBnaXZlcyBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvcgpwcnVuZWQudHJlZS5taW4gPC0gcHJ1bmUoY2xhc3NfbW9kZWwsIGNwID0gbWluLmNwKQoKIyBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlIHdpdGggdGhlIGNob3NlbiBtaW5pbXVtIGNwCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUubWluLCAKICAgICAgICAgICBleHRyYSA9IDEwNCwgICMgRGlzcGxheXMgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBvbiBub2RlcyAoY2xhc3MgcHJvYmFiaWxpdGllcywgbnVtYmVyIG9mIG9ic2VydmF0aW9ucykKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgICMgR3JlZW4gdG8gQmx1ZSBjb2xvciBzY2hlbWUgZm9yIG5vZGUgYm94ZXMKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgICMgU29saWQgbGluZXMgZm9yIGJyYW5jaGVzCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgICMgU2hhZG93IGNvbG9yIGZvciBub2RlIGxhYmVscwogICAgICAgICAgIG5uID0gVFJVRSwgICMgQWRkIG5vZGUgbnVtYmVycyB0byB0aGUgdHJlZQogICAgICAgICAgIG1haW4gPSAiUHJ1bmVkIENBUlQgQ2xhc3NpZmljYXRpb24gVHJlZSAoTWluIENQKSIgICMgVGl0bGUgZm9yIHRoZSBwbG90CikKCgoKCmBgYAoKRmlndXJlIFUuIFBydW5lZCBDQVJUIENsYXNzaWZpY2F0aW9uIHRyZWUgdXNpbmcgbWluIENQLgoKVGhlIHBydW5lZCBDQVJUIGNsYXNzaWZpY2F0aW9uIHRyZWUgdXNpbmcgdGhlIG1pbmltdW0gQ1AgcmVzdWx0ZWQgaW4gYSBzaW5nbGUgbm9kZSwgc3VnZ2VzdGluZyB0aGF0IHRoaXMgYXBwcm9hY2ggZGlkIG5vdCBwcm9kdWNlIGEgbWVhbmluZ2Z1bCBvciBpbmZvcm1hdGl2ZSBtb2RlbC4KCgoKIyMgQkFHR0lORyBDYXJ0IENsYXNzaWZpY2F0aW9uCgpUaGUgQkFHR0lORyAoQm9vdHN0cmFwIEFnZ3JlZ2F0aW5nKSBtZXRob2QgaXMgYXBwbGllZCB0byB0aGUgQ0FSVCAoQ2xhc3NpZmljYXRpb24gYW5kIFJlZ3Jlc3Npb24gVHJlZXMpIG1vZGVsIHRvIHByZWRpY3QgVGVuWWVhckNIRCwgYSBiaW5hcnkgb3V0Y29tZSBpbmRpY2F0aW5nIHRoZSBsaWtlbGlob29kIG9mIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2Ugd2l0aGluIHRlbiB5ZWFycy4gQkFHR0lORyBjb21iaW5lcyBtdWx0aXBsZSBkZWNpc2lvbiB0cmVlcyBidWlsdCBvbiBib290c3RyYXBwZWQgc3Vic2V0cyBvZiB0aGUgZGF0YSwgcmVkdWNpbmcgdmFyaWFuY2UgYW5kIGltcHJvdmluZyBtb2RlbCByb2J1c3RuZXNzLiBCeSBhdmVyYWdpbmcgdGhlIHByZWRpY3Rpb25zIG9mIGluZGl2aWR1YWwgdHJlZXMsIEJBR0dJTkcgaGVscHMgbWl0aWdhdGUgb3ZlcmZpdHRpbmcsIG1ha2luZyBpdCBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBjb21wbGV4IGRhdGFzZXRzIGxpa2UgdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuIFRoZSBhbmFseXNpcyBleHBsb3JlcyB0aGUgbW9kZWzigJlzIHBlcmZvcm1hbmNlIHRocm91Z2ggY3Jvc3MtdmFsaWRhdGlvbiwgdHVuaW5nIGh5cGVycGFyYW1ldGVycywgYW5kIGV2YWx1YXRpbmcgdGhlIHJlc3VsdHMgYmFzZWQgb24gUk1TRSBhbmQgUi1zcXVhcmVkLCB1bHRpbWF0ZWx5IGlkZW50aWZ5aW5nIHRoZSBvcHRpbWFsIHBhcmFtZXRlcnMgZm9yIGFjY3VyYXRlIHByZWRpY3Rpb25zLgoKCkZpcnN0LCB0aGUgQkFHR0lORyBtb2RlbCBpcyB0cmFpbmVkIHVzaW5nIHRoZSBjaG9zZW4gdHJhaW5pbmcgZGF0YSBhbmQgYWdncmVnYXRlIHRoZSBwcmVkaWN0aW9ucyBmcm9tIGVhY2ggZGVjaXNpb24gdHJlZSB0byBtYWtlIHRoZSBmaW5hbCBjbGFzc2lmaWNhdGlvbiwgZW5zdXJpbmcgdGhhdCBlYWNoIG1vZGVs4oCZcyBwcmVkaWN0aW9uIGNvbnRyaWJ1dGVzIHRvIHRoZSBvdmVyYWxsIG91dHB1dC4KCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBFbnN1cmUgdGhlIHRhcmdldCBpcyBhIGZhY3RvcgpmaHMkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IoZmhzJFRlblllYXJDSEQpCgojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihmaHMkVGVuWWVhckNIRCwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbkRhdGEgPC0gZmhzW3RyYWluSW5kZXgsIF0KdGVzdERhdGEgPC0gZmhzWy10cmFpbkluZGV4LCBdCgojIENyZWF0ZSBoeXBlcnBhcmFtZXRlciBncmlkCmh5cGVyLmdyaWQgPC0gZXhwYW5kLmdyaWQoCiAgbmJhZ2cgPSBjKDI1LCA1MCwgMTAwKSwKICBtaW5zcGxpdCA9IGMoNSwgMTAsIDIwKSwKICBtYXhkZXB0aCA9IGMoNSwgMTAsIDIwKSwKICBjcCA9IGMoMC4wMSwgMC4wMDEpCikKCiMgSW5pdGlhbGl6ZSB0cmFja2luZyB2YXJpYWJsZXMKcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkKYmVzdC5hY2N1cmFjeSA8LSAwCmJlc3QucGFyYW1zIDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggaHlwZXJwYXJhbWV0ZXIgY29tYmluYXRpb25zCmZvcihpIGluIDE6bnJvdyhoeXBlci5ncmlkKSkgewogIHBhcmFtcyA8LSBoeXBlci5ncmlkW2ksIF0KICAKICBjdHJsIDwtIHJwYXJ0LmNvbnRyb2woCiAgICBtaW5zcGxpdCA9IHBhcmFtcyRtaW5zcGxpdCwKICAgIG1heGRlcHRoID0gcGFyYW1zJG1heGRlcHRoLAogICAgY3AgPSBwYXJhbXMkY3AKICApCiAgCiAgIyBUcmFpbiBCYWdnaW5nIG1vZGVsCiAgYmFnLm1vZGVsIDwtIGJhZ2dpbmcoCiAgICBUZW5ZZWFyQ0hEIH4gLiwKICAgIGRhdGEgPSB0cmFpbkRhdGEsCiAgICBuYmFnZyA9IHBhcmFtcyRuYmFnZywKICAgIGNvb2IgPSBUUlVFLAogICAgY29udHJvbCA9IGN0cmwKICApCiAgCiAgIyBQcmVkaWN0IG9uIHRlc3Qgc2V0CiAgcHJlZHMgPC0gcHJlZGljdChiYWcubW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YSkKICAKICAjIEFjY3VyYWN5CiAgY20gPC0gY29uZnVzaW9uTWF0cml4KHByZWRzLCB0ZXN0RGF0YSRUZW5ZZWFyQ0hEKQogIGFjY3VyYWN5IDwtIGNtJG92ZXJhbGxbIkFjY3VyYWN5Il0KICAKICAjIFN0b3JlIHJlc3VsdHMKICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUoCiAgICBuYmFnZyA9IHBhcmFtcyRuYmFnZywKICAgIG1pbnNwbGl0ID0gcGFyYW1zJG1pbnNwbGl0LAogICAgbWF4ZGVwdGggPSBwYXJhbXMkbWF4ZGVwdGgsCiAgICBjcCA9IHBhcmFtcyRjcCwKICAgIEFjY3VyYWN5ID0gYWNjdXJhY3kKICApKQogIAogICMgVHJhY2sgYmVzdCByZXN1bHQKICBpZihhY2N1cmFjeSA+IGJlc3QuYWNjdXJhY3kpIHsKICAgIGJlc3QuYWNjdXJhY3kgPC0gYWNjdXJhY3kKICAgIGJlc3QucGFyYW1zIDwtIHBhcmFtcwogIH0KfQoKIyBEaXNwbGF5IGJlc3QgcGFyYW1ldGVycwpjYXQoIkJlc3QgQkFHR0lORyBoeXBlcnBhcmFtZXRlcnM6XG4iKQpwYW5kZXIoYmVzdC5wYXJhbXMpCgojIE9wdGlvbmFsOiBWaWV3IGZ1bGwgcmVzdWx0cwojIHBhbmRlcihyZXN1bHRzKQoKCmBgYAoKVGFibGUgMTAuIFRyYWluIGZpbmFsIEJBR0dJTkcgQ2xhc3NpZmljYXRpb24gbW9kZWwgd2l0aCBiZXN0IGh5cGVycGFyYW1ldGVycwoKClRoZSBvdXRwdXQgc2hvd3MgdGhlIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGZvciB0aGUgQkFHR0lORyBtb2RlbCwgd2l0aCAxMDAgdHJlZXMgKG5iYWdnKSwgYSBtaW5pbXVtIHNwbGl0IG9mIDIwIG9ic2VydmF0aW9ucyAobWluc3BsaXQpLCBhIG1heGltdW0gZGVwdGggb2YgMjAgZm9yIGVhY2ggdHJlZSAobWF4ZGVwdGgpLCBhbmQgYSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIG9mIDAuMDAxLgoKCk5leHQgdGhlIGZpbmFsIEJBR0dJTkcgbW9kZWwgaXMgdHJhaW5lZCB1c2luZyB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMgaWRlbnRpZmllZCBkdXJpbmcgdHVuaW5nLiBUaGUgbW9kZWwgaXMgdGhlbiB1c2VkIHRvIHByZWRpY3QgdGhlIHRlc3QgZGF0YSwgYW5kIHRoZSByZXN1bHRzIGFyZSBldmFsdWF0ZWQgdXNpbmcgYSBsYWJlbGVkIGNvbmZ1c2lvbiBtYXRyaXggdG8gYXNzZXNzIHRoZSBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeSBmb3IgcHJlZGljdGluZyBUZW5ZZWFyQ0hELgoKYGBge3J9CiMgU2V0IHJwYXJ0IGNvbnRyb2wgdXNpbmcgYmVzdCBwYXJhbWV0ZXJzIGZyb20gdHVuaW5nCmJlc3QuY29udHJvbCA8LSBycGFydC5jb250cm9sKAogIG1pbnNwbGl0ID0gYmVzdC5wYXJhbXMkbWluc3BsaXQsCiAgbWF4ZGVwdGggPSBiZXN0LnBhcmFtcyRtYXhkZXB0aCwKICBjcCA9IGJlc3QucGFyYW1zJGNwCikKCiMgVHJhaW4gdGhlIGZpbmFsIEJhZ2dpbmcgbW9kZWwgb24gdHJhaW5pbmcgZGF0YQpmaW5hbC5iYWcubW9kZWwgPC0gYmFnZ2luZygKICBUZW5ZZWFyQ0hEIH4gLiwKICBkYXRhID0gdHJhaW5EYXRhLAogIG5iYWdnID0gYmVzdC5wYXJhbXMkbmJhZ2csCiAgY29vYiA9IFRSVUUsCiAgY29udHJvbCA9IGJlc3QuY29udHJvbAopCgojIEdlbmVyYXRlIHByZWRpY3Rpb25zIChwcm9iYWJpbGl0aWVzKSBmb3IgdGhlIEJhZ2dpbmcgbW9kZWwKcHJlZC5iYWcucHJvYiA8LSBwcmVkaWN0KGZpbmFsLmJhZy5tb2RlbCwgbmV3ZGF0YSA9IHRlc3REYXRhLCB0eXBlID0gInByb2IiKVssIDJdICAjIFsgLDJdIHNlbGVjdHMgdGhlIHByb2JhYmlsaXR5IGZvciB0aGUgcG9zaXRpdmUgY2xhc3MgKDEpCgojIENvbXB1dGUgUk9DIGZvciBCYWdnaW5nIG1vZGVsCnJvYy5iYWdnaW5nIDwtIHJvYyh0ZXN0RGF0YSRUZW5ZZWFyQ0hELCBwcmVkLmJhZy5wcm9iKQoKIyBDb21wdXRlIEFVQyBmb3IgQmFnZ2luZyBtb2RlbAphdWMuYmFnZ2luZyA8LSByb2MuYmFnZ2luZyRhdWMKCiMgUGxvdCBST0MgY3VydmUgZm9yIHRoZSBCYWdnaW5nIG1vZGVsCiNwbG90KHJvYy5iYWdnaW5nLCBtYWluID0gIlJPQyBDdXJ2ZSAtIEJhZ2dpbmciLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCgojIE5vdyBwcm9jZWVkIHdpdGggY29tcGFyaW5nIG90aGVyIG1vZGVscyBhbmQgcGxvdHRpbmcgYXMgbmVlZGVkCgoKYGBgCgoKCiMjIFJhbmRvbSBGb3Jlc3QgQ2xhc3NpZmljYXRpb24KClJhbmRvbSBGb3Jlc3QgY2xhc3NpZmljYXRpb24gaXMgYW4gZW5zZW1ibGUgbGVhcm5pbmcgbWV0aG9kIHRoYXQgYnVpbGRzIG11bHRpcGxlIGRlY2lzaW9uIHRyZWVzIGFuZCBhZ2dyZWdhdGVzIHRoZWlyIHByZWRpY3Rpb25zIHRvIGltcHJvdmUgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kuIEVhY2ggdHJlZSBpcyB0cmFpbmVkIG9uIGEgcmFuZG9tIHN1YnNldCBvZiB0aGUgZGF0YSB3aXRoIHJhbmRvbSBmZWF0dXJlIHNlbGVjdGlvbiBhdCBlYWNoIHNwbGl0LCB3aGljaCBoZWxwcyByZWR1Y2Ugb3ZlcmZpdHRpbmcgYW5kIGluY3JlYXNlcyB0aGUgbW9kZWzigJlzIHJvYnVzdG5lc3MuIFRoZSBmaW5hbCBwcmVkaWN0aW9uIGlzIGRldGVybWluZWQgYnkgdGhlIG1ham9yaXR5IHZvdGUgb2YgdGhlIGluZGl2aWR1YWwgdHJlZXMsIG1ha2luZyBSYW5kb20gRm9yZXN0IGEgcG93ZXJmdWwgdG9vbCBmb3IgY2xhc3NpZmljYXRpb24gdGFza3MsIGVzcGVjaWFsbHkgd2hlbiBkZWFsaW5nIHdpdGggY29tcGxleCBhbmQgaGlnaC1kaW1lbnNpb25hbCBkYXRhLgoKSHlwZXJwYXJhbWV0ZXIgdHVuaW5nIGludm9sdmVzIHNlbGVjdGluZyB0aGUgb3B0aW1hbCBzZXR0aW5ncyBmb3IgYSBtb2RlbOKAmXMgcGFyYW1ldGVycywgc3VjaCBhcyB0aGUgbnVtYmVyIG9mIHRyZWVzLCB0aGUgbWF4aW11bSBkZXB0aCBvZiBlYWNoIHRyZWUsIGFuZCB0aGUgbWluaW11bSBudW1iZXIgb2Ygc2FtcGxlcyByZXF1aXJlZCBmb3IgYSBzcGxpdCwgdG8gZW5oYW5jZSBpdHMgcGVyZm9ybWFuY2UgYW5kIGFjY3VyYWN5LiAKCmBgYHtyfQoKc2V0LnNlZWQoMTIzKQoKIyBEcm9wIEJQTWVkc19vcmlnaW5hbCBmcm9tIGJvdGggc2V0cwp0cmFpbi5kYXRhIDwtIHRyYWluLmRhdGFbLCAhY29sbmFtZXModHJhaW4uZGF0YSkgJWluJSAiQlBNZWRzX29yaWdpbmFsIl0KdGVzdC5kYXRhIDwtIHRlc3QuZGF0YVssICFjb2xuYW1lcyh0ZXN0LmRhdGEpICVpbiUgIkJQTWVkc19vcmlnaW5hbCJdCgojIEVuc3VyZSBzYW1lIGNvbHVtbnMgYW5kIGNvbHVtbiBvcmRlcgp0ZXN0LmRhdGEgPC0gdGVzdC5kYXRhWywgbmFtZXModHJhaW4uZGF0YSldCgojIEVuc3VyZSBhbGwgZmFjdG9yIGxldmVscyBhcmUgYWxpZ25lZApmb3IgKGNvbCBpbiBuYW1lcyh0cmFpbi5kYXRhKSkgewogIGlmIChpcy5mYWN0b3IodHJhaW4uZGF0YVtbY29sXV0pKSB7CiAgICB0ZXN0LmRhdGFbW2NvbF1dIDwtIGZhY3Rvcih0ZXN0LmRhdGFbW2NvbF1dLCBsZXZlbHMgPSBsZXZlbHModHJhaW4uZGF0YVtbY29sXV0pKQogIH0KfQoKIyBUd28td2F5IHNwbGl0OiB0cmFpbmluZy90ZXN0aW5nCnRyYWluLmlkeCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGZocyRUZW5ZZWFyQ0hELCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluLmRhdGEgPC0gZmhzW3RyYWluLmlkeCwgXQp0ZXN0LmRhdGEgPC0gZmhzWy10cmFpbi5pZHgsIF0KCnRlc3QuZGF0YSA8LSB0ZXN0LmRhdGFbLCBuYW1lcyh0cmFpbi5kYXRhKV0KCiMgRW5zdXJlIG91dGNvbWUgaXMgZmFjdG9yIHdpdGggbGV2ZWxzICIwIiwgIjEiCnRyYWluLmRhdGEkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IodHJhaW4uZGF0YSRUZW5ZZWFyQ0hEKQp0ZXN0LmRhdGEkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IodGVzdC5kYXRhJFRlblllYXJDSEQpCiMgRW5zdXJlICdCUE1lZHNfb3JpZ2luYWwnIGlzIG5vdCBpbiB0aGUgZGF0YQp0cmFpbi5kYXRhIDwtIHRyYWluLmRhdGFbLCAhY29sbmFtZXModHJhaW4uZGF0YSkgJWluJSAiQlBNZWRzX29yaWdpbmFsIl0KCiMgQ3Jvc3MtdmFsaWRhdGlvbiBzZXR1cAprIDwtIDUKdHJhaW4uc2l6ZSA8LSBucm93KHRyYWluLmRhdGEpCmZvbGQuc2l6ZSA8LSBmbG9vcih0cmFpbi5zaXplIC8gaykKCiMgSHlwZXJwYXJhbWV0ZXIgZ3JpZAp0dW5lLmdyaWQgPC0gZXhwYW5kLmdyaWQoCiAgbXRyeSA9IGMoMiwgMywgNCwgNSksCiAgbnRyZWUgPSBjKDEwMCwgMzAwLCA1MDApLAogIG5vZGVzaXplID0gYygxLCAzLCA1LCAxMCksCiAgbWF4bm9kZXMgPSBjKDUsIDEwLCAyMCwgTkEpCikKCiMgRW5zdXJlIHZhbGlkIG10cnkgdmFsdWVzIGJlZm9yZSBzdGFydGluZwpwIDwtIG5jb2wodHJhaW4uZGF0YSkgLSAxICAjIE51bWJlciBvZiBwcmVkaWN0b3JzCnR1bmUuZ3JpZCA8LSBzdWJzZXQodHVuZS5ncmlkLCBtdHJ5ID49IDEgJiBtdHJ5IDw9IHAgJiBub2Rlc2l6ZSA8IG1heG5vZGVzKQoKIyBJbml0aWFsaXplIHJlc3VsdHMgc3RvcmFnZQpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoKQpiZXN0LmF1YyA8LSAwCmJlc3QuaHlwLnBhcmFtcyA8LSBOVUxMCgojIEdyaWQgc2VhcmNoIHdpdGggY3Jvc3MtdmFsaWRhdGlvbgpmb3IgKGkgaW4gMTpucm93KHR1bmUuZ3JpZCkpIHsKICBjdXJyZW50LnR1bmUucGFyYW1zIDwtIHR1bmUuZ3JpZFtpLCBdCiAgY3YuYXVjIDwtIG51bWVyaWMoaykgICMgUHJlYWxsb2NhdGUgQVVDIHZlY3RvciBmb3IgZm9sZHMKICAKICBmb3IgKGogaW4gMTprKSB7CiAgICAjIERlZmluZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXRzCiAgICBjdi5pZCA8LSAoKGogLSAxKSAqIGZvbGQuc2l6ZSArIDEpOihqICogZm9sZC5zaXplKQogICAgY3YudHJhaW4gPC0gdHJhaW4uZGF0YVstY3YuaWQsIF0KICAgIGN2LnZhbGlkIDwtIHRyYWluLmRhdGFbY3YuaWQsIF0KICAgIAogICAgIyBUcmFpbiB0aGUgbW9kZWwKICAgIHJmLmN2IDwtIHJhbmRvbUZvcmVzdCgKICAgICAgVGVuWWVhckNIRCB+IC4sCiAgICAgIGRhdGEgPSBjdi50cmFpbiwKICAgICAgbXRyeSA9IGN1cnJlbnQudHVuZS5wYXJhbXMkbXRyeSwKICAgICAgbnRyZWUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG50cmVlLAogICAgICBub2Rlc2l6ZSA9IGN1cnJlbnQudHVuZS5wYXJhbXMkbm9kZXNpemUsCiAgICAgIG1heG5vZGVzID0gY3VycmVudC50dW5lLnBhcmFtcyRtYXhub2RlcwogICAgKQogICAgCiAgICAjIFByZWRpY3QgcHJvYmFiaWxpdGllcyBmb3IgcG9zaXRpdmUgY2xhc3MgJzEnCiAgICBwcm9iLmN2IDwtIHByZWRpY3QocmYuY3YsIG5ld2RhdGEgPSBjdi52YWxpZCwgdHlwZSA9ICJwcm9iIilbLCAiMSJdCiAgICAKICAgICMgQ29tcHV0ZSBBVUMgdXNpbmcgcFJPQwogICAgcm9jX29iaiA8LSBwUk9DOjpyb2MoCiAgICAgIHJlc3BvbnNlID0gY3YudmFsaWQkVGVuWWVhckNIRCwKICAgICAgcHJlZGljdG9yID0gcHJvYi5jdiwKICAgICAgZGlyZWN0aW9uID0gIjwiCiAgICApCiAgICBjdi5hdWNbal0gPC0gcFJPQzo6YXVjKHJvY19vYmopCiAgfQogIAogICMgQ29tcHV0ZSBhdmVyYWdlIEFVQyBvdmVyIGZvbGRzCiAgYXZnLmF1YyA8LSBtZWFuKGN2LmF1YykKICAKICAjIFN0b3JlIHJlc3VsdHMKICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUoCiAgICBtdHJ5ID0gY3VycmVudC50dW5lLnBhcmFtcyRtdHJ5LAogICAgbnRyZWUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG50cmVlLAogICAgbm9kZXNpemUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG5vZGVzaXplLAogICAgbWF4bm9kZXMgPSBjdXJyZW50LnR1bmUucGFyYW1zJG1heG5vZGVzLAogICAgYXVjID0gYXZnLmF1YwogICkpCiAgCiAgIyBVcGRhdGUgYmVzdCBpZiBjdXJyZW50IGlzIGJldHRlcgogIGlmIChhdmcuYXVjID4gYmVzdC5hdWMpIHsKICAgIGJlc3QuYXVjIDwtIGF2Zy5hdWMKICAgIGJlc3QuaHlwLnBhcmFtcyA8LSBjdXJyZW50LnR1bmUucGFyYW1zCiAgfQp9CgojIERpc3BsYXkgYmVzdCBwYXJhbWV0ZXJzCnBhbmRlcihkYXRhLmZyYW1lKGNiaW5kKGJlc3QuaHlwLnBhcmFtcywgYmVzdC5hdWMpKSkKCgoKYGBgCgpUYWJsZSAxMS4gUmFuZG9tIEZvcmVzdCBIeXBlcnBhcmFtZXRlciBUdW5pbmcgUmVzdWx0cyBUYWJsZQoKClRoZSBvdXRwdXQgaW5kaWNhdGVzIHRoZSBvcHRpbWFsIGh5cGVycGFyYW1ldGVycyBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwsIHdpdGggbXRyeSBzZXQgdG8gNCwgbnRyZWUgc2V0IHRvIDUwMCwgbm9kZXNpemUgc2V0IHRvIDEwLCBhbmQgbWF4bm9kZXMgc2V0IHRvIDIwLiBUaGVzZSBzZXR0aW5ncyByZXN1bHRlZCBpbiBhIGJlc3QgQVVDIG9mIDAuNjk2NSwgd2hpY2ggcmVmbGVjdHMgdGhlIG1vZGVs4oCZcyBwZXJmb3JtYW5jZSBpbiBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIHRoZSBjbGFzc2VzLgoKCgpUbyBmaW5hbGl6ZSB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCwgdGhlIGJlc3QgaHlwZXJwYXJhbWV0ZXJzIGlkZW50aWZpZWQgZnJvbSB0dW5pbmcgYXJlIHVzZWQgdG8gdHJhaW4gdGhlIG1vZGVsIG9uIHRoZSBmdWxsIHRyYWluaW5nIGRhdGFzZXQuIFByZWRpY3Rpb25zIGZvciBjbGFzcyBwcm9iYWJpbGl0aWVzIG9mIHRoZSBwb3NpdGl2ZSBjbGFzcyAo4oCcMeKAnSkgYXJlIHRoZW4gbWFkZSBmb3IgdGhlIHRlc3QgZGF0YS4gSW4gdGhlIHN1YnNlcXVlbnQgc3RlcHMsIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2Ugd2lsbCBiZSBhc3Nlc3NlZCB1c2luZyB0aGUgUk9DIGN1cnZlIGFuZCBBVUMsIHdoaWNoIHdpbGwgYmUgcHJlc2VudGVkIGR1cmluZyB0aGUgY29tcGFyaXNvbiBzZWN0aW9uLgoKCmBgYHtyfQoKIyBUcmFpbiB0aGUgZmluYWwgUmFuZG9tIEZvcmVzdCBtb2RlbCB1c2luZyBiZXN0IHBhcmFtZXRlcnMKZmluYWwucmYuY2xzIDwtIHJhbmRvbUZvcmVzdCgKICBUZW5ZZWFyQ0hEIH4gLiwKICBkYXRhID0gdHJhaW4uZGF0YSwKICBudHJlZSA9IGJlc3QuaHlwLnBhcmFtcyRudHJlZSwKICBtdHJ5ID0gYmVzdC5oeXAucGFyYW1zJG10cnksCiAgbm9kZXNpemUgPSBiZXN0Lmh5cC5wYXJhbXMkbm9kZXNpemUsCiAgbWF4bm9kZXMgPSBiZXN0Lmh5cC5wYXJhbXMkbWF4bm9kZXMsCiAgaW1wb3J0YW5jZSA9IFRSVUUKKQoKIyBQcmVkaWN0IGNsYXNzIHByb2JhYmlsaXRpZXMgZm9yIHRoZSBwb3NpdGl2ZSBjbGFzcyAiMSIKcHJlZC5yZi5wcm9iIDwtIHByZWRpY3QoZmluYWwucmYuY2xzLCB0ZXN0LmRhdGEsIHR5cGUgPSAicHJvYiIpWywgIjEiXQoKIyBFbnN1cmUgcmVzcG9uc2UgaXMgYSBmYWN0b3Igd2l0aCBjb3JyZWN0IGxldmVscwp0ZXN0LnJlc3BvbnNlIDwtIGZhY3Rvcih0ZXN0LmRhdGEkVGVuWWVhckNIRCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgQ29tcHV0ZSBST0MgYW5kIEFVQwpyZi5yb2MgPC0gcFJPQzo6cm9jKHJlc3BvbnNlID0gdGVzdC5yZXNwb25zZSwgcHJlZGljdG9yID0gcHJlZC5yZi5wcm9iLCBsZXZlbHMgPSBjKCIwIiwgIjEiKSwgZGlyZWN0aW9uID0gIjwiKQpyZi5hdWMgPC0gcFJPQzo6YXVjKHJmLnJvYykKCiMgRGlzcGxheSBBVUMKI3ByaW50KHJmLmF1YykKCgoKYGBgCgoKCgoKClRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIGluIHRoZSBmaW5hbCBSYW5kb20gRm9yZXN0IG1vZGVsIGlzIGFzc2Vzc2VkIHVzaW5nIHR3byBtZXRyaWNzOiBNZWFuIERlY3JlYXNlIGluIEFjY3VyYWN5IGFuZCBNZWFuIERlY3JlYXNlIGluIEdpbmkuIFRoZXNlIG1lYXN1cmVzIGFyZSBleHRyYWN0ZWQgYW5kIHZpc3VhbGl6ZWQgdG8gaGlnaGxpZ2h0IHRoZSBtb3N0IGluZmx1ZW50aWFsIHZhcmlhYmxlcywgcHJvdmlkaW5nIGluc2lnaHRzIGludG8gd2hpY2ggZmVhdHVyZXMgY29udHJpYnV0ZSB0aGUgbW9zdCB0byB0aGUgbW9kZWzigJlzIHByZWRpY3RpdmUgcG93ZXIuIFRoZSByZXN1bHRzIGFyZSBkaXNwbGF5ZWQgaW4gYSBiYXIgcGxvdCB0aGF0IHNlcGFyYXRlcyB0aGUgaW1wb3J0YW5jZSBzY29yZXMgZm9yIGVhY2ggbWV0cmljLgoKYGBge3J9CiMgRXh0cmFjdCBpbXBvcnRhbmNlIG1lYXN1cmVzCmltcG9ydGFuY2VfdmFscyA8LSBpbXBvcnRhbmNlKGZpbmFsLnJmLmNscykKaW1wb3J0YW5jZV9kZiA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gcm93bmFtZXMoaW1wb3J0YW5jZV92YWxzKSwKICBNZWFuRGVjcmVhc2VBY2N1cmFjeSA9IGltcG9ydGFuY2VfdmFsc1ssICJNZWFuRGVjcmVhc2VBY2N1cmFjeSJdLAogIE1lYW5EZWNyZWFzZUdpbmkgPSBpbXBvcnRhbmNlX3ZhbHNbLCAiTWVhbkRlY3JlYXNlR2luaSJdCikKCgojIFBpdm90IGZvciBwbG90dGluZwppbXBvcnRhbmNlX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKAogIGltcG9ydGFuY2VfZGYsCiAgY29scyA9IGMoTWVhbkRlY3JlYXNlQWNjdXJhY3ksIE1lYW5EZWNyZWFzZUdpbmkpLAogIG5hbWVzX3RvID0gIk1ldHJpYyIsCiAgdmFsdWVzX3RvID0gIkltcG9ydGFuY2UiCikKCiMgUGxvdApnZ3Bsb3QoaW1wb3J0YW5jZV9sb25nLCBhZXMoeCA9IHJlb3JkZXIoVmFyaWFibGUsIEltcG9ydGFuY2UpLCB5ID0gSW1wb3J0YW5jZSwgZmlsbCA9IE1ldHJpYykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF93cmFwKH4gTWV0cmljLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGxhYnModGl0bGUgPSAiVmFyaWFibGUgSW1wb3J0YW5jZSAoUmFuZG9tIEZvcmVzdCkiLAogICAgICAgeCA9ICJWYXJpYWJsZSIsCiAgICAgICB5ID0gIkltcG9ydGFuY2UgU2NvcmUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKYGBgCgpGaWd1cmUgVi4gUmFuZG9tIEZvcmVzdCBGZWF0dXJlIEltcG9ydGFuY2UgVmlzdWFsaXphdGlvbgoKCiMjIENvbXBhcmlzb24gCgpUbyBldmFsdWF0ZSBhbmQgY29tcGFyZSB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvZiB0aGUgcHJ1bmVkIENBUlQgbW9kZWxzLCBsb2dpc3RpYyByZWdyZXNzaW9uLCBCYWdnaW5nLCBhbmQgUmFuZG9tIEZvcmVzdCwgd2UgZ2VuZXJhdGVkIFJPQyAoUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljKSBjdXJ2ZXMgdXNpbmcgdGhlIHRlc3QgZGF0YXNldC4gVGhlc2UgY3VydmVzIGlsbHVzdHJhdGUgdGhlIHRyYWRlLW9mZiBiZXR3ZWVuIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBhY3Jvc3MgYWxsIHBvc3NpYmxlIGNsYXNzaWZpY2F0aW9uIHRocmVzaG9sZHMsIHByb3ZpZGluZyBhIHZpc3VhbCBjb21wYXJpc29uIG9mIG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBkaWZmZXJlbnQgbWV0aG9kcy4KCgoKYGBge3J9CgoKIyBGaXQgdGhlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwKbG9naXRfbW9kZWwgPC0gZ2xtKFRlblllYXJDSEQgfiAuLCBkYXRhID0gdHJhaW4uZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpCgojIFByZWRpY3QgcHJvYmFiaWxpdGllcyBmb3IgdGhlIHRlc3Qgc2V0CnByZWQubG9naXQucHJvYiA8LSBwcmVkaWN0KGxvZ2l0X21vZGVsLCBuZXdkYXRhID0gdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikKCiMgRW5zdXJlIHJlc3BvbnNlIGlzIGEgZmFjdG9yIHdpdGggY29ycmVjdCBsZXZlbHMKdGVzdC5yZXNwb25zZSA8LSBmYWN0b3IodGVzdC5kYXRhJFRlblllYXJDSEQsIGxldmVscyA9IGMoMCwgMSkpCgojIENvbXB1dGUgUk9DIGFuZCBBVUMgZm9yIExvZ2lzdGljIFJlZ3Jlc3Npb24KbG9naXQucm9jIDwtIHJvYyhyZXNwb25zZSA9IHRlc3QucmVzcG9uc2UsIHByZWRpY3RvciA9IHByZWQubG9naXQucHJvYiwgbGV2ZWxzID0gYygiMCIsICIxIiksIGRpcmVjdGlvbiA9ICI8IikKbG9naXQuYXVjIDwtIHBST0M6OmF1Yyhsb2dpdC5yb2MpCgoKIyBSZWNvbXB1dGUgcHJlZGljdGlvbiwgZW5zdXJlIGl0J3MgYWxpZ25lZCBhbmQgdW4tbmFtZWQKcHJlZC5wcm9iLjFTRSA8LSBwcmVkaWN0KHBydW5lZC50cmVlLjFTRSwgbmV3ZGF0YSA9IHRlc3QuZGF0YSwgdHlwZSA9ICJwcm9iIilbLCAyXQpwcmVkLnByb2IuMVNFIDwtIGFzLm51bWVyaWMocHJlZC5wcm9iLjFTRSkgICMgcmVtb3ZlcyBhbnkgcm93IG5hbWVzCgojIFJPQyBmb3IgZWFjaCBtb2RlbCAoZW5zdXJlIHRoZSBtb2RlbCBwcm9iYWJpbGl0aWVzIGFyZSBkZWZpbmVkIGZpcnN0KQpyb2MudHJlZS4xU0UgPC0gcm9jKHRlc3QuZGF0YSRUZW5ZZWFyQ0hELCBwcmVkLnByb2IuMVNFKQoKcHJlZC5wcm9iLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSwgdHlwZSA9ICJwcm9iIilbLCAyXQpwcmVkLnByb2IubWluIDwtIGFzLm51bWVyaWMocHJlZC5wcm9iLm1pbikKCnJvYy50cmVlLm1pbiA8LSByb2ModGVzdC5kYXRhJFRlblllYXJDSEQsIHByZWQucHJvYi5taW4pCnJvYy5iYWdnaW5nIDwtIHJvYyh0ZXN0LmRhdGEkVGVuWWVhckNIRCwgcHJlZC5iYWcucHJvYikKcm9jLnJmIDwtIHJvYyh0ZXN0LmRhdGEkVGVuWWVhckNIRCwgcHJlZC5yZi5wcm9iKQpyb2MubG9naXQgPC0gcm9jKHRlc3QuZGF0YSRUZW5ZZWFyQ0hELCBwcmVkLmxvZ2l0LnByb2IpCgojIEFVQyB2YWx1ZXMKYXVjLnRyZWUuMVNFIDwtIHBST0M6OmF1Yyhyb2MudHJlZS4xU0UpCmF1Yy50cmVlLm1pbiA8LSBwUk9DOjphdWMocm9jLnRyZWUubWluKQphdWMuYmFnZ2luZyA8LSBwUk9DOjphdWMocm9jLmJhZ2dpbmcpCmF1Yy5yZiA8LSBwUk9DOjphdWMocm9jLnJmKQphdWMubG9naXQgPC0gcFJPQzo6YXVjKHJvYy5sb2dpdCkKCiMgUGxvdCBST0MgY3VydmVzIGZvciBjb21wYXJpc29uCnBhcihtYXIgPSBjKDUsIDUsIDQsIDEpKQpwbG90KDEgLSByb2MubG9naXQkc3BlY2lmaWNpdGllcywgcm9jLmxvZ2l0JHNlbnNpdGl2aXRpZXMsIAogICAgIHR5cGUgPSAibCIsIGNvbCA9ICJkYXJrb3JhbmdlIiwgbHdkID0gMiwgCiAgICAgeGxhYiA9ICIxIC0gU3BlY2lmaWNpdHkiLCB5bGFiID0gIlNlbnNpdGl2aXR5IiwgCiAgICAgbWFpbiA9ICJST0MgQ3VydmUgQ29tcGFyaXNvbiIpCgpsaW5lcygxIC0gcm9jLnRyZWUuMVNFJHNwZWNpZmljaXRpZXMsIHJvYy50cmVlLjFTRSRzZW5zaXRpdml0aWVzLCBjb2wgPSAiZ29sZCIsIGx3ZCA9IDIpCmxpbmVzKDEgLSByb2MudHJlZS5taW4kc3BlY2lmaWNpdGllcywgcm9jLnRyZWUubWluJHNlbnNpdGl2aXRpZXMsIGNvbCA9ICJwdXJwbGUiLCBsd2QgPSAyKQpsaW5lcygxIC0gcm9jLmJhZ2dpbmckc3BlY2lmaWNpdGllcywgcm9jLmJhZ2dpbmckc2Vuc2l0aXZpdGllcywgY29sID0gImJsdWUiLCBsd2QgPSAyKQpsaW5lcygxIC0gcm9jLnJmJHNwZWNpZmljaXRpZXMsIHJvYy5yZiRzZW5zaXRpdml0aWVzLCBjb2wgPSAiZm9yZXN0Z3JlZW4iLCBsd2QgPSAyKQoKIyBBZGQgYSBsZWdlbmQgZm9yIGNsYXJpdHkKbGVnZW5kKCJib3R0b21yaWdodCIsCiAgICAgICBsZWdlbmQgPSBjKCJMb2dpc3RpYyIsICJDQVJUIDFTRSIsICJDQVJUIE1pbiIsICJSYW5kb20gRm9yZXN0IiwgIkJhZ2dpbmciKSwKICAgICAgIGNvbCA9IGMoImRhcmtvcmFuZ2UiLCAiZ29sZCIsICJwdXJwbGUiLCAiYmx1ZSIsICJmb3Jlc3RncmVlbiIpLAogICAgICAgbHR5ID0gYygxLCAxLCAxLCAxLCAxKSwKICAgICAgIGx3ZCA9IDIsCiAgICAgICBidHkgPSAibiIsCiAgICAgICBjZXggPSAwLjYpCgojIyMgPT09IEFubm90YXRlIHdpdGggQVVDcyA9PT0gIyMjCiMgQW5ub3RhdGluZyB0aGUgcGxvdCB3aXRoIHRoZSBBVUMgdmFsdWVzCnRleHQoMC43NSwgMC40OCwgcGFzdGUoIkxvZ2lzdGljIEFVQzogIiwgcm91bmQoYXVjLmxvZ2l0LCA0KSksIGNleCA9IDAuNykKdGV4dCgwLjc1LCAwLjQyLCBwYXN0ZSgiQ0FSVCAxU0UgQVVDOiAiLCByb3VuZChhdWMudHJlZS4xU0UsIDQpKSwgY2V4ID0gMC43KQp0ZXh0KDAuNzUsIDAuMzYsIHBhc3RlKCJDQVJUIE1pbiBBVUM6ICIsIHJvdW5kKGF1Yy50cmVlLm1pbiwgNCkpLCBjZXggPSAwLjcpCnRleHQoMC43NSwgMC4zMCwgcGFzdGUoIlJhbmRvbSBGb3Jlc3QgQVVDOiAiLCByb3VuZChhdWMucmYsIDQpKSwgY2V4ID0gMC43KQp0ZXh0KDAuNzUsIDAuMjQsIHBhc3RlKCJCYWdnaW5nIEFVQzogIiwgcm91bmQoYXVjLmJhZ2dpbmcsIDQpKSwgY2V4ID0gMC43KQoKYGBgCgoKCkZpZ3VyZSBXLiBST0MgQ3VydmVzIGZvciBDbGFzc2lmaWNhdGlvbiBNb2RlbHMuIAoKUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIEN1cnZlcyBmb3IgTXVsdGlwbGUgUHJlZGljdGl2ZSBNb2RlbHM6IFRoZSBwbG90IGNvbXBhcmVzIHRoZSBST0MgY3VydmVzIGZvciBMb2dpc3RpYyBSZWdyZXNzaW9uLCBDQVJUIHdpdGggMS1TRSBwcnVuaW5nLCBDQVJUIHdpdGggTWluaW11bSBDb3N0LUNvbXBsZXhpdHkgUHJ1bmluZywgQmFnZ2luZywgYW5kIFJhbmRvbSBGb3Jlc3QuIEFVQyB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgYXJlIGFubm90YXRlZCwgaGlnaGxpZ2h0aW5nIHRoZWlyIGNsYXNzaWZpY2F0aW9uIHBlcmZvcm1hbmNlLiBBbW9uZyB0aGUgbW9kZWxzIGV2YWx1YXRlZCwgTG9naXN0aWMgUmVncmVzc2lvbiBhY2hpZXZlZCB0aGUgaGlnaGVzdCBBVUMsIGRlbW9uc3RyYXRpbmcgc3VwZXJpb3IgYWNjdXJhY3kgaW4gZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBpbmRpdmlkdWFscyB3aG8gd2lsbCBhbmQgd2lsbCBub3QgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIG92ZXIgYSB0ZW4teWVhciBwZXJpb2QuIFRoaXMgc3VnZ2VzdHMgdGhhdCwgaW4gdGhpcyBzY2VuYXJpbywgTG9naXN0aWMgUmVncmVzc2lvbiBwcm92aWRlcyBhIG1vcmUgcmVsaWFibGUgcmlzayBwcmVkaWN0aW9uIHRvb2wgY29tcGFyZWQgdG8gdGhlIG90aGVyIG1vZGVscy4KCiMjIE9wdGltYWwgQ3V0b2ZmCgpUaGUgb3B0aW1hbCBjdXRvZmYgcmVwcmVzZW50cyB0aGUgdGhyZXNob2xkIHByb2JhYmlsaXR5IGF0IHdoaWNoIHRoZSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBiYWxhbmNlcyBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHksIG1heGltaXppbmcgdGhlIG92ZXJhbGwgcGVyZm9ybWFuY2Ugb2YgdGhlIG1vZGVsLiBCeSB1c2luZyB0aGlzIGN1dG9mZiwgd2UgZW5zdXJlIHRoYXQgcHJlZGljdGlvbnMgYXJlIG1hZGUgd2l0aCB0aGUgbW9zdCBlZmZlY3RpdmUgdHJhZGUtb2ZmIGJldHdlZW4gY29ycmVjdGx5IGlkZW50aWZ5aW5nIHBvc2l0aXZlIGNhc2VzIGFuZCBtaW5pbWl6aW5nIGZhbHNlIHBvc2l0aXZlcywgd2hpY2ggaXMgY3J1Y2lhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGRlY2lzaW9ucyBpbiBwcmFjdGljYWwgYXBwbGljYXRpb25zIHN1Y2ggYXMgaGVhbHRoY2FyZSBvciBmaW5hbmNlLgoKCmBgYHtyfQojIEZpbmQgdGhlIG9wdGltYWwgY3V0b2ZmIHVzaW5nIFlvdWRlbidzIEogc3RhdGlzdGljCmNvb3Jkcy5vcHRpbWFsIDwtIGNvb3Jkcyhyb2MubG9naXQsIHggPSAiYmVzdCIsIGJlc3QubWV0aG9kID0gInlvdWRlbiIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQoKIyBQcmludCB0aGUgb3B0aW1hbCBjdXRvZmYgYW5kIGFzc29jaWF0ZWQgc2Vuc2l0aXZpdHkvc3BlY2lmaWNpdHkKcHJpbnQoY29vcmRzLm9wdGltYWwpCgpgYGAKCgpUYWJsZSAxMi4gVGhlIG9wdGltYWwgY3V0b2ZmLiAKCgpUaGUgdGhyZXNob2xkIHZhbHVlIG9mIDAuMTU2MzcxOSBpcyB0aGUgb3B0aW1hbCBjdXRvZmYuIEl0IGlzIGNob3NlbiB0byBiYWxhbmNlIHRoZSBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHkuIElmIHlvdSBhcmUgdXNpbmcgdGhpcyB0aHJlc2hvbGQsIHRoZSBtb2RlbCB3aWxsIGNsYXNzaWZ5IGEgcGVyc29uIGFzIGF0IHJpc2sgZm9yIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UgaWYgdGhlaXIgcHJlZGljdGVkIHByb2JhYmlsaXR5IGlzIGdyZWF0ZXIgdGhhbiAxNS42JS4gQSBzZW5zaXRpdml0eSBvZiA2My4yJSBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgaXMgZmFpcmx5IGdvb2QgYXQgaWRlbnRpZnlpbmcgaW5kaXZpZHVhbHMgd2hvIHdpbGwgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlLiBIb3dldmVyLCBpdCBtaXNzZXMgYWJvdXQgMzYuOCUgb2YgdGhlIHRydWUgcG9zaXRpdmVzIChmYWxzZSBuZWdhdGl2ZXMpLiBPbiB0aGUgb3RoZXIgaGFuZCwgdGhlIHNwZWNpZmljaXR5IG9mIDcwLjMlIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBkb2VzIGEgYmV0dGVyIGpvYiBvZiBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgaW5kaXZpZHVhbHMgd2hvIHdpbGwgbm90IGRldmVsb3AgdGhlIGRpc2Vhc2UsIHRob3VnaCB0aGVyZSBpcyBzdGlsbCBzb21lIHJvb20gZm9yIGltcHJvdmVtZW50IChhYm91dCAyOS43JSBmYWxzZSBwb3NpdGl2ZXMpLgoKCiMgQ09OQ0xVU0lPTgoKVGhlIHJlZ3Jlc3Npb24gbW9kZWxzIGV2YWx1YXRlZCwgaW5jbHVkaW5nIFBydW5lZCBUcmVlcyAoYm90aCAxLVNFIGFuZCBNaW5pbXVtIENvc3QtQ29tcGxleGl0eSksIEJhZ2dpbmcsIGFuZCBSYW5kb20gRm9yZXN0LCBwcm92aWRlZCB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHByZWRpY3RpbmcgdG90YWwgY2hvbGVzdGVyb2wgbGV2ZWxzICh0b3RDaG9sKS4gQW1vbmcgdGhlc2UsIHRoZSBSYW5kb20gRm9yZXN0IG1vZGVsIGRlbW9uc3RyYXRlZCB0aGUgYmVzdCBwZXJmb3JtYW5jZSB3aXRoIHRoZSBsb3dlc3QgUk1TRSBhbmQgaGlnaGVzdCBSLXNxdWFyZWQgdmFsdWUuIFRoZSBQcnVuZWQgVHJlZSAoTWluaW11bSBDb3N0LUNvbXBsZXhpdHkpIG1vZGVsLCB3aGlsZSBzdGlsbCBwZXJmb3JtaW5nIHdlbGwsIHNob3dlZCBhIGhpZ2hlciBSTVNFLCBzdWdnZXN0aW5nIHRoYXQgaXRzIHByZWRpY3Rpb25zIHdlcmUgbGVzcyBhY2N1cmF0ZS4gR2l2ZW4gdGhlIG5hdHVyZSBvZiB0aGUgdGFza+KAlHByZWRpY3RpbmcgYSBjb250aW51b3VzIHZhcmlhYmxlIHN1Y2ggYXMgY2hvbGVzdGVyb2wgbGV2ZWxz4oCUaXQgaXMgcmVjb21tZW5kZWQgdG8gdXRpbGl6ZSBSYW5kb20gRm9yZXN0IGFzIHRoZSBwcmltYXJ5IG1vZGVsIGZvciBmdXR1cmUgcHJlZGljdGlvbnMgZHVlIHRvIGl0cyBoaWdoZXIgYWNjdXJhY3kgYW5kIHJvYnVzdG5lc3MuIEhvd2V2ZXIsIGZvciBpbnRlcnByZXRhYmlsaXR5IGFuZCBzaW1wbGljaXR5LCBQcnVuZWQgVHJlZXMgbWF5IGFsc28gYmUgY29uc2lkZXJlZCB3aGVuIGEgbW9yZSB0cmFuc3BhcmVudCBtb2RlbCBpcyBuZWVkZWQuCgoKRm9yIGNsYXNzaWZpY2F0aW9uLCB0aGUgY29tcGFyaXNvbiBvZiBtb2RlbHMgc3VjaCBhcyBMb2dpc3RpYyBSZWdyZXNzaW9uLCBDQVJUICgxLVNFIGFuZCBNaW4pLCBCYWdnaW5nLCBhbmQgUmFuZG9tIEZvcmVzdCBmb3IgcHJlZGljdGluZyBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIChUZW5ZZWFyQ0hEKSByZXZlYWxlZCB0aGF0IExvZ2lzdGljIFJlZ3Jlc3Npb24gb3V0cGVyZm9ybWVkIGFsbCBvdGhlciBtb2RlbHMsIGFjaGlldmluZyB0aGUgaGlnaGVzdCBBVUMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBMb2dpc3RpYyBSZWdyZXNzaW9uIGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgYXQgZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBpbmRpdmlkdWFscyB3aG8gd2lsbCBhbmQgd2lsbCBub3QgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIGluIHRoZSBuZXh0IHRlbiB5ZWFycy4gV2hpbGUgUmFuZG9tIEZvcmVzdCBhbmQgQmFnZ2luZyBzaG93ZWQgc3Ryb25nIHBlcmZvcm1hbmNlLCBMb2dpc3RpYyBSZWdyZXNzaW9uIHByb3ZpZGVkIGEgbW9yZSByZWxpYWJsZSBhbmQgc2ltcGxlciBtb2RlbCBmb3IgdGhpcyB0YXNrLiBUaGVyZWZvcmUsIGl0IGlzIHJlY29tbWVuZGVkIHRvIHByaW9yaXRpemUgTG9naXN0aWMgUmVncmVzc2lvbiBmb3IgZnV0dXJlIHVzZSBpbiBjbGluaWNhbCBhcHBsaWNhdGlvbnMgd2hlcmUgdW5kZXJzdGFuZGluZyBhbmQgZXhwbGFpbmluZyB0aGUgcmlzayBvZiBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIGlzIGNyaXRpY2FsLiBGb3IgaW1wcm92ZWQgcGVyZm9ybWFuY2UgaW4gbW9yZSBjb21wbGV4IGRhdGFzZXRzIG9yIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcywgUmFuZG9tIEZvcmVzdCBhbmQgQmFnZ2luZyBjYW4gYmUgY29uc2lkZXJlZCBhcyBhbHRlcm5hdGl2ZSBtb2RlbHMgZm9yIGZ1cnRoZXIgZXhwbG9yYXRpb24uCgo=